diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 3 - MovieClip/pixi.js b/examples/example 3 - MovieClip/pixi.js index e760dbf..9068c9e 100755 --- a/examples/example 3 - MovieClip/pixi.js +++ b/examples/example 3 - MovieClip/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 3 - MovieClip/pixi.js b/examples/example 3 - MovieClip/pixi.js index e760dbf..9068c9e 100755 --- a/examples/example 3 - MovieClip/pixi.js +++ b/examples/example 3 - MovieClip/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 4 - Balls/pixi.js b/examples/example 4 - Balls/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 4 - Balls/pixi.js +++ b/examples/example 4 - Balls/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 3 - MovieClip/pixi.js b/examples/example 3 - MovieClip/pixi.js index e760dbf..9068c9e 100755 --- a/examples/example 3 - MovieClip/pixi.js +++ b/examples/example 3 - MovieClip/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 4 - Balls/pixi.js b/examples/example 4 - Balls/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 4 - Balls/pixi.js +++ b/examples/example 4 - Balls/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 5 - Morph/pixi.js b/examples/example 5 - Morph/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 5 - Morph/pixi.js +++ b/examples/example 5 - Morph/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 3 - MovieClip/pixi.js b/examples/example 3 - MovieClip/pixi.js index e760dbf..9068c9e 100755 --- a/examples/example 3 - MovieClip/pixi.js +++ b/examples/example 3 - MovieClip/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 4 - Balls/pixi.js b/examples/example 4 - Balls/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 4 - Balls/pixi.js +++ b/examples/example 4 - Balls/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 5 - Morph/pixi.js b/examples/example 5 - Morph/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 5 - Morph/pixi.js +++ b/examples/example 5 - Morph/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 6 - Interactivity/pixi.js b/examples/example 6 - Interactivity/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 6 - Interactivity/pixi.js +++ b/examples/example 6 - Interactivity/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 3 - MovieClip/pixi.js b/examples/example 3 - MovieClip/pixi.js index e760dbf..9068c9e 100755 --- a/examples/example 3 - MovieClip/pixi.js +++ b/examples/example 3 - MovieClip/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 4 - Balls/pixi.js b/examples/example 4 - Balls/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 4 - Balls/pixi.js +++ b/examples/example 4 - Balls/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 5 - Morph/pixi.js b/examples/example 5 - Morph/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 5 - Morph/pixi.js +++ b/examples/example 5 - Morph/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 6 - Interactivity/pixi.js b/examples/example 6 - Interactivity/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 6 - Interactivity/pixi.js +++ b/examples/example 6 - Interactivity/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 7 - Transparent Background/pixi.js b/examples/example 7 - Transparent Background/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 7 - Transparent Background/pixi.js +++ b/examples/example 7 - Transparent Background/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 3 - MovieClip/pixi.js b/examples/example 3 - MovieClip/pixi.js index e760dbf..9068c9e 100755 --- a/examples/example 3 - MovieClip/pixi.js +++ b/examples/example 3 - MovieClip/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 4 - Balls/pixi.js b/examples/example 4 - Balls/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 4 - Balls/pixi.js +++ b/examples/example 4 - Balls/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 5 - Morph/pixi.js b/examples/example 5 - Morph/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 5 - Morph/pixi.js +++ b/examples/example 5 - Morph/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 6 - Interactivity/pixi.js b/examples/example 6 - Interactivity/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 6 - Interactivity/pixi.js +++ b/examples/example 6 - Interactivity/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 7 - Transparent Background/pixi.js b/examples/example 7 - Transparent Background/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 7 - Transparent Background/pixi.js +++ b/examples/example 7 - Transparent Background/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 8 - Dragging/pixi.js b/examples/example 8 - Dragging/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 8 - Dragging/pixi.js +++ b/examples/example 8 - Dragging/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index e760dbf..9068c9e 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/bin/pixi.js b/bin/pixi.js index cf762bd..0b0059d 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,14 +1,15 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -!function(){function c(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}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.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.constructor=f.Rectangle,f.Polygon=function(a){this.points=a},f.Polygon.clone=function(){for(var a=[],b=0;b=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);void 0!=a.parent&&a.parent.removeChild(a),b==this.children.length?this.children.push(a):this.children.splice(b,0,a),a.parent=this,a.childIndex=b;for(var c=this.children.length,d=b;c>d;d++)this.children[d].childIndex=d;this.stage&&this.stage.__addChild(a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(a,b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(-1===c||-1===d)throw new Error(a+" Both the supplied DisplayObjects must be a child of the caller "+this);this.stage&&(this.stage.__removeChild(a),this.stage.__removeChild(b),this.stage.__addChild(a),this.stage.__addChild(b)),a.childIndex=d,b.childIndex=c,this.children[c]=b,this.children[d]=a},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&ac;c++)this.children[c].childIndex-=1},f.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){f.DisplayObject.prototype.updateTransform.call(this);for(var a=0,b=this.children.length;b>a;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.constructor=f.Sprite,f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.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.currentFrame=0,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.playing},f.MovieClip.constructor=f.MovieClip,f.MovieClip.prototype=Object.create(f.Sprite.prototype),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.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.constructor=f.Text,f.Text.prototype=Object.create(f.Sprite.prototype),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.Sprite.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.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.mouse=new f.InteractionData,this.touchs={},this.pool=[],this.interactiveItems=[],this.last=0},f.InteractionManager.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.visible&&(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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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,this.interactiveItems.length;for(var c=0;cc;c++){var e=this.interactiveItems[c];e.visible&&(e.mouseover||e.mouseout||e.buttonMode)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit?(e.buttonMode&&(this.target.view.style.cursor="pointer"),e.__isOver||(e.mouseover&&e.mouseover(this.mouse),e.__isOver=!0)):e.__isOver&&(e.mouseout&&e.mouseout(this.mouse),e.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){var b=this.target.view.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){a.preventDefault();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.onMouseUp=function(){this.mouse.global;for(var a=this.interactiveItems.length,b=!1,c=0;a>c;c++){var d=this.interactiveItems[c];(d.mouseup||d.mouseupoutside||d.click)&&(d.__hit=this.hitTest(d,this.mouse),d.__hit&&!b?(d.mouseup&&d.mouseup(this.mouse),d.__isDown&&d.click&&d.click(this.mouse),d.interactiveChildren||(b=!0)):d.__isDown&&d.mouseupoutside&&d.mouseupoutside(this.mouse),d.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.visible)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(a.hitArea){var p=a.hitArea;if(a.hitArea instanceof f.Polygon){for(var q=!1,r=0,s=a.hitArea.points.length-1;ro!=w>o&&(v-t)*(o-u)/(w-u)+t>n;x&&(q=!q)}if(q)return d&&(b.target=a),!0}else{var y=p.x;if(n>y&&nz&&oy&&y+A>n&&(z=-B*a.anchor.y,o>z&&z+B>o))return b.target=a,!0}for(var C=a.children.length,r=0;C>r;r++){var D=a.children[r],E=this.hitTest(D,b);if(E)return!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){a.preventDefault();for(var b=this.target.view.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.target.view.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&((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},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.constructor=f.InteractionData,f.Stage=function(a,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.__childrenAdded=[],this.__childrenRemoved=[],this.childIndex=0,this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.interactive=!!b,this.interactionManager=new f.InteractionManager(this),this.setBackgroundColor(a),this.worldVisible=!0,this.stage.dirty=!0},f.Stage.constructor=f.Stage,f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.updateTransform=function(){this.worldAlpha=1;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=c(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},f.Stage.prototype.__addChild=function(a){if(a.interactive&&(this.dirty=!0),a.stage=this,a.children)for(var b=0;bb;b++)this.__removeChild(a.children[b])};for(var h=0,i=["ms","moz","webkit","o"],j=0;j0){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);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):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):j instanceof f.TilingSprite?j.visible&&this.renderTilingSprite(j,b):j instanceof f.Strip?j.visible&&this.renderStrip(j,b):j instanceof f.CustomRenderable&&j.visible&&j.renderWebGL(this,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():renderable instanceof f.TilingSprite?renderable.visible&&this.renderTilingSprite(renderable,b):renderable instanceof f.Strip?renderable.visible&&this.renderStrip(renderable,b):renderable instanceof f.CustomRenderable&&renderable.visible&&renderable.renderWebGL(this,b);l instanceof f.WebGLBatch?l.render(0,g+1):l instanceof f.TilingSprite?l.visible&&this.renderTilingSprite(l):l instanceof f.Strip?l.visible&&this.renderStrip(l):l instanceof f.CustomRenderable&&l.visible&&l.renderWebGL(this,b)},f.WebGLRenderGroup.prototype.checkVisibility=function(a,b){for(var c=a.children,d=0;d0&&this.checkVisibility(e,e.worldVisible)}},f.WebGLRenderGroup.prototype.updateTexture=function(a){if(1==a.batch.length)return a.batch.texture=a.texture.baseTexture,void 0;if(a.batch.texture!=a.texture.baseTexture)if(a.batch.head==a){var b=a.batch,c=this.batchs.indexOf(b),d=this.batchs[c-1];if(b.remove(a),d)if(d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)d.insertAfter(a,d.tail);else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c-1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(0,0,e)}}else if(a.batch.tail==a){var b=a.batch,c=this.batchs.indexOf(b),g=this.batchs[c+1];if(b.remove(a),g){if(g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertBefore(a,g.head),void 0;var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.splice(c+1,0,e)}else{var e=f.WebGLRenderer.getBatch();e.init(a),this.batchs.push(e)}}else{var b=a.batch,h=b.split(a);h.remove(a);var e=f.WebGLRenderer.getBatch(),c=this.batchs.indexOf(b);e.init(a),this.batchs.splice(c+1,0,e,h)}},f.WebGLRenderGroup.prototype.addDisplayObject=function(a){if(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),a.__renderGroup=this,a.renderable){var b=this.getPreviousRenderable(a),c=this.getNextRenderable(a);if(a instanceof f.Sprite){var d,e;if(b instanceof f.Sprite){if(d=b.batch,d&&d.texture==a.texture.baseTexture&&d.blendMode==a.blendMode)return d.insertAfter(a,b),void 0}else d=b;if(c)if(c instanceof f.Sprite){if(e=c.batch){if(e.texture==a.texture.baseTexture&&e.blendMode==a.blendMode)return e.insertBefore(a,c),void 0;if(e==d){var g=d.split(c),h=f.WebGLRenderer.getBatch(),i=this.batchs.indexOf(d);return h.init(a),this.batchs.splice(i+1,0,h,g),void 0}}}else e=c;var h=f.WebGLRenderer.getBatch();if(h.init(a),d){var i=this.batchs.indexOf(d);this.batchs.splice(i+1,0,h)}else this.batchs.push(h)}else a instanceof f.TilingSprite?(this.initTilingSprite(a),this.batchs.push(a)):a instanceof f.Strip&&(this.initStrip(a),this.batchs.push(a));this.batchUpdate=!0}},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){this.addDisplayObject(a);for(var b=a.children,c=0;c0&&(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=a.worldTransform,c=this.context;if(a.visible){if(a instanceof f.Sprite){var d=a.texture.frame;d&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,d.x,d.y,d.width,d.height,a.anchor.x*-d.width,a.anchor.y*-d.height,d.width,d.height))}else a instanceof f.Strip?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a)):a instanceof f.TilingSprite?(c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a)):a instanceof f.CustomRenderable&&a.renderCanvas(this);if(a.children)for(var e=0;ee;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;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.Strip=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=f.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(d){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=b,this.height=c,a.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},f.Strip.constructor=f.Strip,f.Strip.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},f.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Rope=function(a,b){f.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},f.Rope.constructor=f.Rope,f.Rope.prototype=Object.create(f.Strip.prototype),f.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=this.indices,d=this.colors,e=a[0],f=a[0];this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,d[0]=1,d[1]=1,c[0]=0,c[1]=1;for(var g=a.length,h=1;g>h;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.renderable=!0,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.blendMode=f.blendModes.NORMAL},f.TilingSprite.constructor=f.TilingSprite,f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),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.count=0,this.sprites=[],this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData);for(var b=0;b>1),d+=-(b.attachment.height*(b.bone.worldScaleY+b.attachment.scaleY-1)>>1),this.sprites[a].position.x=c,this.sprites[a].position.y=d,this.sprites[a].rotation=-(b.bone.worldRotation+b.attachment.rotation)*(Math.PI/180)}f.DisplayObjectContainer.prototype.updateTransform.call(this)};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/2},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={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:0}},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"],e=new l.RegionAttachment;return e.name=b,d==l.AttachmentType.region&&(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},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[Math.floor(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.constructor=f.CustomRenderable,f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),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.source=a,a){if(this.source instanceof Image)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.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)),this.trim=new f.Point,a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,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.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.constructor=f.RenderTexture,f.RenderTexture.prototype=Object.create(f.Texture.prototype),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.projectionMatrix=f.mat4.create(),this.projectionMatrix[5]=2/this.height,this.projectionMatrix[13]=-1,this.projectionMatrix[0]=2/this.width,this.projectionMatrix[12]=-1,this.render=this.renderWebGL},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){var c=f.gl;c.colorMask(!0,!0,!0,!0),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,this.glFramebuffer),b&&(c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT));var d=a.children;a.worldTransform=f.mat3.create();for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();var h=a.__renderGroup;h?a==h.root?h.render(this.projectionMatrix):h.renderSpecific(a,this.projectionMatrix):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(c)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projectionMatrix))},f.RenderTexture.prototype.renderCanvas=function(a,b){var c=a.children;a.worldTransform=f.mat3.create();for(var d=0,e=c.length;e>d;d++)c[d].updateTransform();b&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),f.texturesToUpdate.push(this.baseTexture)},f.AssetLoader=function(a){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=!1,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.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;b>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){this._mask=a,a?this.addFilter(a):this.removeFilter()}}),f.DisplayObject.prototype.addFilter=function(a){if(!this.filter){this.filter=!0;var b=new f.FilterBlock,c=new f.FilterBlock;b.mask=a,c.mask=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),a.renderable=!1}},f.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var a=this.first,b=a._iNext,c=a._iPrev;b&&(b._iPrev=c),c&&(c._iNext=b),this.first=a._iNext;var d=this.last,b=d._iNext,c=d._iPrev;b&&(b._iPrev=c),c._iNext=b;for(var e=d._iPrev,f=this;f.last==d&&(f.last=e,f=f.parent););var g=a.mask;g.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(a,d)}},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.filter?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.__renderGroup&&(this.texture=a,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,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(a){this.graphics=a,this.visible=!0,this.renderable=!0},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.Sprite.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.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){window.navigator.msPointerEnabled&&(a.view.style["-ms-content-zooming"]="none",a.view.style["-ms-touch-action"]="none"),this.target=a,a.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),a.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),a.view.addEventListener("mouseout",this.onMouseOut.bind(this),!0),a.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),a.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),a.view.addEventListener("touchmove",this.onTouchMove.bind(this),!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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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,b){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=b,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.updateTransform=function(){this.worldAlpha=1;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){for(var c in a[b.type])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.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(){var a=this.gl,b=f.compileProgram(f.shaderVertexSrc,f.shaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.shaderProgram=b},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.activateDefaultShader=function(){var a=f.gl,b=f.shaderProgram;a.useProgram(b),a.enableVertexAttribArray(b.vertexPositionAttribute),a.enableVertexAttribArray(b.textureCoordAttribute),a.enableVertexAttribArray(b.colorAttribute) +},f.activatePrimitiveShader=function(){var a=f.gl;a.disableVertexAttribArray(f.shaderProgram.textureCoordAttribute),a.disableVertexAttribArray(f.shaderProgram.colorAttribute),a.useProgram(f.primitiveProgram),a.enableVertexAttribArray(f.primitiveProgram.vertexPositionAttribute),a.enableVertexAttribArray(f.primitiveProgram.colorAttribute)},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.shaderProgram.vertexPositionAttribute,2,c.FLOAT,!1,0,0),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.activateDefaultShader()},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.initPrimitiveShader(),f.initDefaultShader(),f.initDefaultStripShader(),f.activateDefaultShader();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,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);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){var c=a.vcount===f.visibleCount;if(a instanceof f.TilingSprite)c&&this.renderTilingSprite(a,b);else if(a instanceof f.Strip)c&&this.renderStrip(a,b);else if(a instanceof f.CustomRenderable)c&&a.renderWebGL(this,b);else if(a instanceof f.Graphics)c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b);else if(a instanceof f.FilterBlock){var d=f.gl;a.open?(d.enable(d.STENCIL_TEST),d.colorMask(!1,!1,!1,!1),d.stencilFunc(d.ALWAYS,1,255),d.stencilOp(d.KEEP,d.KEEP,d.REPLACE),f.WebGLGraphics.renderGraphics(a.mask,b),d.colorMask(!0,!0,!0,!0),d.stencilFunc(d.NOTEQUAL,0,255),d.stencilOp(d.KEEP,d.KEEP,d.KEEP)):d.disable(d.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&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root&&(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.shaderProgram;c.useProgram(f.stripShaderProgram);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(f.stripShaderProgram.translationMatrix,!1,e),c.uniform2f(f.stripShaderProgram.projectionVector,b.x,b.y),c.uniform1f(f.stripShaderProgram.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.shaderProgram)},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=[],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&&(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)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;b= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 10 - Text/pixi.js b/examples/example 10 - Text/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 10 - Text/pixi.js +++ b/examples/example 10 - Text/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 11 - RenderTexture/pixi.js b/examples/example 11 - RenderTexture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 11 - RenderTexture/pixi.js +++ b/examples/example 11 - RenderTexture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 12 - Spine/pixi.js b/examples/example 12 - Spine/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 12 - Spine/pixi.js +++ b/examples/example 12 - Spine/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 2 - SpriteSheet/pixi.js b/examples/example 2 - SpriteSheet/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 2 - SpriteSheet/pixi.js +++ b/examples/example 2 - SpriteSheet/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 3 - MovieClip/pixi.js b/examples/example 3 - MovieClip/pixi.js index e760dbf..9068c9e 100755 --- a/examples/example 3 - MovieClip/pixi.js +++ b/examples/example 3 - MovieClip/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 4 - Balls/pixi.js b/examples/example 4 - Balls/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 4 - Balls/pixi.js +++ b/examples/example 4 - Balls/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 5 - Morph/pixi.js b/examples/example 5 - Morph/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 5 - Morph/pixi.js +++ b/examples/example 5 - Morph/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 6 - Interactivity/pixi.js b/examples/example 6 - Interactivity/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 6 - Interactivity/pixi.js +++ b/examples/example 6 - Interactivity/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 7 - Transparent Background/pixi.js b/examples/example 7 - Transparent Background/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 7 - Transparent Background/pixi.js +++ b/examples/example 7 - Transparent Background/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 8 - Dragging/pixi.js b/examples/example 8 - Dragging/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 8 - Dragging/pixi.js +++ b/examples/example 8 - Dragging/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ diff --git a/examples/example 9 - Tiling Texture/pixi.js b/examples/example 9 - Tiling Texture/pixi.js index e760dbf..9068c9e 100644 --- a/examples/example 9 - Tiling Texture/pixi.js +++ b/examples/example 9 - Tiling Texture/pixi.js @@ -1,14 +1,14 @@ -/** - * @license - * Pixi.JS - v1.2.0 - * Copyright (c) 2012, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2013-06-19 - * - * Pixi.JS is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ +/** + * @license + * Pixi.JS - v1.3.0 + * Copyright (c) 2012, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2013-08-18 + * + * Pixi.JS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -16,22 +16,23 @@ (function(){ var root = this; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** -@module PIXI + * @module PIXI */ var PIXI = PIXI || {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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 * @param x {Number} position of the point @@ -54,9 +55,11 @@ this.y = y || 0; } -/** +/** + * Creates a clone of this point + * * @method clone - * @return a copy of the point + * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { @@ -64,21 +67,22 @@ } // constructor -PIXI.Point.constructor = PIXI.Point; +PIXI.Point.prototype.constructor = PIXI.Point; - + /** * @author Mat Groves http://matgroves.com/ */ /** * 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 - * @param x {Number} position of the rectangle - * @param y {Number} position of the rectangle - * @param width {Number} of the rectangle - * @param height {Number} of the rectangle + * @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 height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { @@ -111,19 +115,48 @@ this.height = height || 0; } -/** +/** + * Creates a clone of this Rectangle + * * @method clone - * @return a copy of the rectangle + * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } -// constructor -PIXI.Rectangle.constructor = PIXI.Rectangle; +/** + * Checks if the x, and y coords passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; - + var x1 = this.x; + if(x >= 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 */ @@ -131,18 +164,40 @@ /** * @class Polygon * @constructor - * @param points {Array} + * @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 + * 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 a copy of the polygon + * @return {Polygon} a copy of the polygon */ -PIXI.Polygon.clone = function() +PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; + if(intersect) inside = !inside; } -});*/ -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * @method setInteractive - * @param interactive {Boolean} - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; + return inside; } +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + /** - * @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; - - ///AAARR GETTER SETTTER! - //localTransform[2] = this.position.x; - //localTransform[5] = this.position.y; - - var px = this.pivot.x; - var py = this.pivot.y; - - ///AAARR GETTER SETTTER! - localTransform[2] = this.position.x - localTransform[0] * px - py * localTransform[1]; - localTransform[5] = this.position.y - localTransform[4] * py - px * localTransform[3]; - - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = localTransform[2], - a10 = localTransform[3], a11 = localTransform[4], a12 = localTransform[5], - - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; - - 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; - - -} - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ - /** - * 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 + * 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.DisplayObjectContainer = function() +PIXI.Circle = function(x, y, radius) { - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The of children of this container. - * @property children {Array} - */ - this.children = []; - //s - this.renderable = false; -} - -// constructor -PIXI.DisplayObjectContainer.constructor = PIXI.DisplayObjectContainer; -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); - -//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. - * @method addChild - * @param DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - child.parent = this; - child.childIndex = this.children.length; - - this.children.push(child); - - if(this.stage) - { - this.stage.__addChild(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); - } -} - -/** - * 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 DisplayObject {DisplayObject} - * @param index {Number} - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent != undefined) - { - child.parent.removeChild(child); - } - - if (index == this.children.length) - { - this.children.push(child); - } - else - { - this.children.splice(index, 0, child); - } - - child.parent = this; - child.childIndex = index; - - var length = this.children.length; - for (var i=index; i < length; i++) - { - this.children[i].childIndex = i; - } - - if(this.stage) - { - this.stage.__addChild(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 - { - // error! - - throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); - } -} - -/** - * Swaps the depth of 2 displayObjects - * @method swapChildren - * @param DisplayObject {DisplayObject} - * @param DisplayObject2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - // 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 indexes.. - child.childIndex = index2; - child2.childIndex = index; - // 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} - */ -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 DisplayObject {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - - if ( index !== -1 ) - { - if(this.stage) - { - this.stage.__removeChild(child); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } - - // console.log(">" + child.__renderGroup) - child.parent = undefined; - - this.children.splice( index, 1 ); - - // update in dexs! - for(var i=index,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -} -/** - * @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 {String} text The copy that you would like the text to display - * @param {Object} [style] The style parameters - * @param {String} [style.font] default "bold 20pt Arial" The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] 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.x = x || 0; - this.updateText(); - this.dirty = false; -}; + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; -// constructor -PIXI.Text.constructor = PIXI.Text; -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} /** - * Set the style of the text - * @method setStyle - * @param {Object} [style] The style parameters - * @param {String} [style.font="bold 20pt Arial"] The style and size of the font - * @param {Object} [style.fill="black"] A canvas fillstyle that will be used on the text eg "red", "#00FF00" - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") - * @param {String} [style.stroke="black"] A canvas fillstyle that will be used on the text stroke eg "blue", "#FCFF00" - * @param {Number} [style.strokeThickness=0] A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param {Boolean} [style.wordWrap=false] Indicates if word wrap should be used - * @param {Number} [style.wordWrapWidth=100] The width at which text will wrap + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon */ -PIXI.Text.prototype.setStyle = function(style) +PIXI.Circle.prototype.clone = function() { - 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; -}; + return new PIXI.Circle(this.x, this.y, this.radius); +} /** - * 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 + * 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.Sprite.prototype.setText = function(text) +PIXI.Circle.prototype.contains = function(x, y) { - this.text = text.toString() || " "; - this.dirty = true; -}; + 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); +} + +PIXI.Circle.prototype.constructor = PIXI.Circle; + /** - * Renders text - * @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 - * @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); -}; - -/** - * @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! - */ -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 - * @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; -}; - -PIXI.Text.prototype.destroy = function(destroyTexture) -{ - if(destroyTexture) - { - this.texture.destroy(); - } - -}; - -PIXI.Text.heightCache = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler */ /** - * 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 + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse * @constructor - * @param {String} text The copy that you would like the text to display - * @param {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] An alignment of the multiline text ("left", "center" or "right") + * @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 */ -PIXI.BitmapText = function(text, style) +PIXI.Ellipse = function(x, y, width, height) { - PIXI.DisplayObjectContainer.call(this); - - this.setText(text); - this.setStyle(style); - this.updateText(); - this.dirty = false - -}; - -// constructor -PIXI.BitmapText.constructor = PIXI.BitmapText; -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); - -/** - * Set the copy for the text object - * @method setText - * @param {String} text 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 {Object} style The style parameters - * @param {String} style.font The size (optional) and bitmap font id (required) eq "Arial" or "20px Arial" (must have loaded previously) - * @param {String} [style.align="left"] 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 - * @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; -}; - -/** - * @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} -@type Stage -*/ -PIXI.InteractionManager = function(stage) -{ - /** - * a refference to the stage - * @property stage - * @type Stage - */ - this.stage = stage; - - // helpers - this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); - - this.mouseoverEnabled = true; - - /** - * 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 = {}; - - //tiny little interactiveData pool! - this.pool = []; - - this.interactiveItems = []; - - this.last = 0; -} - -// constructor -PIXI.InteractionManager.constructor = PIXI.InteractionManager; - -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - //this.interactiveItems = []; - /// 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); - } - } - } - } -} - -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - 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" + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; - // 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); -} - -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 < this.interactiveItems.length; 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.target.view.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); - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) - { - if(item.buttonMode)this.target.view.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; - } - } - } - - // ---> - } -} - -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.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); - } - } -} - -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - event.preventDefault(); - - // 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.onMouseUp = function(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; - } - } -} - -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if(!item.visible)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; - - //a sprite or display object with a hit area defined - if(item.hitArea) - { - var hitArea = item.hitArea; - - //Polygon hit area - if(item.hitArea instanceof PIXI.Polygon) { - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - for(var i = 0, j = item.hitArea.points.length - 1; i < item.hitArea.points.length; j = i++) { - var xi = item.hitArea.points[i].x, yi = item.hitArea.points[i].y, - xj = item.hitArea.points[j].x, yj = item.hitArea.points[j].y, - intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - if(inside) { - if(isSprite) interactionData.target = item; - return true; - } - } - //Rectangle hit area - else { - var x1 = hitArea.x; - if(x > x1 && x < x1 + hitArea.width) - { - var y1 = hitArea.y; - - if(y > y1 && y < y1 + hitArea.height) - { - if(isSprite) interactionData.target = item; - return true; - } - } - } - } - // 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)return true; - } - - return false; -} - - - -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - var rect = this.target.view.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; - - // 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); - } -} - -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - event.preventDefault(); - - var rect = this.target.view.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(); - - 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; - } - } - } - } - -} - -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - var rect = this.target.view.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... - - // 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; - } + /** + * @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; } /** -@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; -} - -/** - * 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 + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.Ellipse.prototype.clone = function() { - 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) + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); } -// constructor -PIXI.InteractionData.constructor = PIXI.InteractionData; - - - /** - * @author Mat Groves http://matgroves.com/ @Doormat23 + * 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 */ - -/** -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 -@param interactive {Boolean} enable / disable interaction (default is false) -*/ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Ellipse.prototype.contains = function(x, y) { - - PIXI.DisplayObjectContainer.call( this ); - this.worldTransform = PIXI.mat3.create() - this.__childrenAdded = []; - this.__childrenRemoved = []; - this.childIndex = 0; - this.stage= this; - - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); - - // interaction! - this.interactive = !!interactive; - this.interactionManager = new PIXI.InteractionManager(this); - - this.setBackgroundColor(backgroundColor); - this.worldVisible = true; - - this.stage.dirty = true; + 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); } -// constructor -PIXI.Stage.constructor = PIXI.Stage; - -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); - -/** -@method updateTransform -@internal -*/ -PIXI.Stage.prototype.updateTransform = function() +PIXI.Ellipse.getBounds = function() { - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -} - -/** - * Provides bind in a cross browser way. - */ -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; - }; - })(); -} - -var AjaxRequest = PIXI.AjaxRequest = function() -{ - var activexmodes = ["Msxml2.XMLHTTP", "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 + * @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) { + + this._mask = value; + + if(value) + { + this.addFilter(value) + } + else + { + this.removeFilter(); + } + } +}); + +/* + * 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(mask) +{ + if(this.filter)return; + this.filter = true; + + // insert a filter block.. + var start = new PIXI.FilterBlock(); + var end = new PIXI.FilterBlock(); + + start.mask = mask; + end.mask = mask; + + 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); + } + + mask.renderable = false; + +} + +/* + * Removes the filter to this displayObject + * + * @method removeFilter + * @private + */ +PIXI.DisplayObject.prototype.removeFilter = function() +{ + if(!this.filter)return; + this.filter = false; + + // modify the list.. + var startBlock = this.first; + + 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 = this.last; + + 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; + } + + var mask = startBlock.mask + mask.renderable = true; + + // 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; + +//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. + * + * @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.filter) + { + 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; + +/** + * 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(mask) +{ + this.graphics = mask + this.visible = true; + this.renderable = true; +} + + +/** + * @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.Sprite.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.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) +{ + 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" + + // 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.onMouseOut.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); +} + +/** + * 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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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.target.view.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 + * @param interactive {Boolean} enable / disable interaction (default is false) + */ +PIXI.Stage = function(backgroundColor, interactive) +{ + 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 = interactive; + + /** + * 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; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + 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", "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 ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( 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 + * 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 {Boolean} the transparency of the render view, default false - * @default false + * @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; @@ -2452,19 +3396,174 @@ //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); }; - + +/* + 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;", @@ -2480,16 +3579,138 @@ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - "uniform mat4 uMVMatrix;", + //"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 = 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;", "}" ]; +/* + * 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.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"); + + 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.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); @@ -2514,7 +3735,572 @@ 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.activateDefaultShader = function() +{ + var gl = PIXI.gl; + var shaderProgram = PIXI.shaderProgram; + + gl.useProgram(shaderProgram); + + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); +} + + + +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); +} + + +/** + * @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 ); + + // return to default shader... + PIXI.activateDefaultShader(); +} + +/** + * 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 */ @@ -2526,76 +4312,87 @@ 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 + * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false + * @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.. - //console.log(transparent) 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; - + 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:false, // SPEED UP?? - premultipliedAlpha:false + 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); } - - this.initShaders(); - - + + PIXI.initPrimitiveShader(); + PIXI.initDefaultShader(); + 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); - - this.projectionMatrix = PIXI.mat4.create(); + + PIXI.projection = new PIXI.Point(400, 300); + this.resize(this.width, this.height); this.contextLost = false; - + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); } // constructor -PIXI.WebGLRenderer.constructor = PIXI.WebGLRenderer; +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** + * Gets a new WebGLBatch from the pool + * + * @static + * @method getBatch + * @return {WebGLBatch} * @private */ PIXI.WebGLRenderer.getBatch = function() @@ -2611,6 +4408,11 @@ } /** + * Puts a batch back into the pool + * + * @static + * @method returnBatch + * @param batch {WebGLBatch} The batch to return * @private */ PIXI.WebGLRenderer.returnBatch = function(batch) @@ -2619,49 +4421,11 @@ PIXI._batchs.push(batch); } - -/** - * @private - */ -PIXI.WebGLRenderer.prototype.initShaders = function() -{ - var gl = this.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, PIXI.shaderFragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, PIXI.shaderVertexSrc); - - PIXI.shaderProgram = gl.createProgram(); - - var shaderProgram = PIXI.shaderProgram; - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert("Could not initialise shaders"); - } - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); - - - shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); -} - - /** * Renders the stage to its webGL view + * * @method render - * @param stage {Stage} the PIXI.Stage element to be rendered + * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { @@ -2673,8 +4437,6 @@ { // 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); } @@ -2691,10 +4453,8 @@ // 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; @@ -2703,17 +4463,15 @@ 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 + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - this.stageRenderGroup.render(this.projectionMatrix); + this.stageRenderGroup.render(PIXI.projection); // interaction // run interaction! @@ -2740,37 +4498,50 @@ } /** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures * @private */ - 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 = []; } +/** + * 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); @@ -2781,16 +4552,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - + gl.bindTexture(gl.TEXTURE_2D, null); } - } -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; - + //TODO break this out into a texture manager... + var gl = PIXI.gl; + if(texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -2800,6 +4578,7 @@ /** * 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 @@ -2808,21 +4587,28 @@ { 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; - - projectionMatrix[0] = 2/this.width; - projectionMatrix[5] = -2/this.height; - projectionMatrix[12] = -1; - projectionMatrix[13] = 1; + + //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) @@ -2832,6 +4618,10 @@ } /** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) @@ -2839,28 +4629,27 @@ 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 */ @@ -2904,10 +4693,15 @@ /** * 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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @constructor + * @param gl {WebGLContext} an instance of the webGL context */ PIXI.WebGLBatch = function(gl) { @@ -2923,12 +4717,13 @@ this.dynamicSize = 1; } - // constructor -PIXI.WebGLBatch.constructor = PIXI.WebGLBatch; +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() { @@ -2936,18 +4731,19 @@ this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; 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 +/** + * Recreates the buffers in the event of a context loss + * + * @method restoreLostContext + * @param gl {WebGLContext} */ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) { @@ -2960,8 +4756,10 @@ /** * 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 + * @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) { @@ -2969,16 +4767,16 @@ 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; - + 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 @@ -2986,13 +4784,13 @@ 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; @@ -3001,12 +4799,12 @@ else { this.head = sprite; - //this.head.__prev = null } } /** * 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 @@ -3014,15 +4812,14 @@ 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; @@ -3032,18 +4829,18 @@ { 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; @@ -3051,7 +4848,7 @@ sprite.__next = null; return; } - + if(sprite.__prev) { sprite.__prev.__next = sprite.__next; @@ -3061,7 +4858,7 @@ this.head = sprite.__next; this.head.__prev = null; } - + if(sprite.__next) { sprite.__next.__prev = sprite.__prev; @@ -3071,7 +4868,7 @@ this.tail = sprite.__prev; this.tail.__next = null } - + sprite.batch = null; sprite.__next = null; sprite.__prev = null; @@ -3080,39 +4877,30 @@ /** * 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) { - - //console.log("Splitting batch :" + this.size) -// console.log(sprite) -// console.log("-------") this.dirty = true; - - //var val = (this.tail == this.head) - //console.log(val + " SAME?"); - 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; - //console.log("id is " +batcheee.id) - + this.tail = sprite.__prev; this.tail.__next = null; - + 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! // 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) @@ -3121,41 +4909,44 @@ 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 - * @methos growBatch + * 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() { @@ -3170,25 +4961,25 @@ } // 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 ) + + 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); - + 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; @@ -3200,152 +4991,152 @@ 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 + 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.worldVisible) + 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; @@ -3354,20 +5145,20 @@ 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; } @@ -3375,39 +5166,36 @@ /** * Draws the batch to the frame buffer + * * @method render */ PIXI.WebGLBatch.prototype.render = function(start, end) { -// console.log(start + " :: " + end + " : " + this.size); start = start || 0; - //end = end || this.size; - if(end == undefined)end = this.size; + 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! - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + var shaderProgram = PIXI.shaderProgram; 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 gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); @@ -3416,12 +5204,12 @@ 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); @@ -3430,34 +5218,34 @@ 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 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 ); } - /** * @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. + * 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 - * @param an instance of the webGL context - * @return {PIXI.renderers.WebGLBatch} WebGLBatch {@link PIXI.renderers.WebGLBatch} + * @contructor + * @param gl {WebGLContext} An instance of the webGL context */ PIXI.WebGLRenderGroup = function(gl) { @@ -3469,10 +5257,16 @@ this.toRemove = []; } - // constructor -PIXI.WebGLRenderGroup.constructor = PIXI.WebGLRenderGroup; +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?? @@ -3485,57 +5279,109 @@ // TODO what if its already has an object? should remove it this.root = displayObject; - //displayObject.__renderGroup = this; this.addDisplayObjectAndChildren(displayObject); - //displayObject } -PIXI.WebGLRenderGroup.prototype.render = function(projectionMatrix) +/** + * 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; + - // set the flipped matrix.. - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); + gl.uniform2f(PIXI.shaderProgram.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; } - 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, projectionMatrix); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); + 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) + { + /* + * 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); + } } } } -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projectionMatrix) +/** + * Renders the stage to its webgl view + * + * @method handleFilter + * @param filter {FilterBlock} + * @private + */ +PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, 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; - this.checkVisibility(displayObject, displayObject.visible); - gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - - - //console.log("SPECIFIC"); + + gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + // to do! // render part of the scene... @@ -3545,8 +5391,18 @@ var endIndex; var endBatchIndex; - // get NEXT Renderable! - var nextRenderable = displayObject.renderable ? displayObject : this.getNextRenderable(displayObject); + /* + * 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) @@ -3615,26 +5471,16 @@ // TODO - need to fold this up a bit! - if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) { startBatch.render(startIndex, endIndex+1); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); + this.renderSpecial(startBatch, projection); } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); - } - return; } @@ -3647,17 +5493,9 @@ { startBatch.render(startIndex); } - else if(startBatch instanceof PIXI.TilingSprite) + else { - if(startBatch.visible)this.renderTilingSprite(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.Strip) - { - if(startBatch.visible)this.renderStrip(startBatch, projectionMatrix); - } - else if(startBatch instanceof PIXI.CustomRenderable) - { - if(startBatch.visible) startBatch.renderWebGL(this, projectionMatrix); + this.renderSpecial(startBatch, projection); } // DO the middle batchs.. @@ -3669,19 +5507,10 @@ { this.batchs[i].render(); } - else if(renderable instanceof PIXI.TilingSprite) + else { - if(renderable.visible)this.renderTilingSprite(renderable, projectionMatrix); + this.renderSpecial(renderable, projection); } - else if(renderable instanceof PIXI.Strip) - { - if(renderable.visible)this.renderStrip(renderable, projectionMatrix); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(renderable.visible) renderable.renderWebGL(this, projectionMatrix); - } - } // DO the last batch.. @@ -3689,184 +5518,269 @@ { endBatch.render(0, endIndex+1); } - else if(endBatch instanceof PIXI.TilingSprite) - { - if(endBatch.visible)this.renderTilingSprite(endBatch); - } - else if(endBatch instanceof PIXI.Strip) - { - if(endBatch.visible)this.renderStrip(endBatch); - } - else if(endBatch instanceof PIXI.CustomRenderable) - { - if(endBatch.visible) endBatch.renderWebGL(this, projectionMatrix); - } -} - -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a refference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... shouldt 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.removeDisplayObject(child); - this.addDisplayObject(child); - //this.updateTexture(child); - } - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - // we know this exists.. - // is it in a batch.. - // check batch length - if(displayObject.batch.length == 1) - { - // just one! this guy! so simply swap the texture - displayObject.batch.texture = displayObject.texture.baseTexture; - return; - } - - // early out! - if(displayObject.batch.texture == displayObject.texture.baseTexture)return; - - - if(displayObject.batch.head == displayObject) - { - //console.log("HEAD") - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var previousBatch = this.batchs[index-1]; - currentBatch.remove(displayObject); - - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousBatch.tail); - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index-1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(0, 0, batch); - } - - } - else if(displayObject.batch.tail == displayObject) - { - var currentBatch = displayObject.batch; - - var index = this.batchs.indexOf( currentBatch ); - var nextBatch = this.batchs[index+1]; - currentBatch.remove(displayObject); - - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextBatch.head); - return; - } - else - { - // add it before.. - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch); - } - - } - else - { - // we are 0! - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - this.batchs.push(batch); - } - } else { - // console.log("MIDDLE") - var currentBatch = displayObject.batch; - - // split the batch into 2 - // AH! dont split on the current display object as the texture is wrong! - var splitBatch = currentBatch.split(displayObject); - - // now remove the display object - splitBatch.remove(displayObject); - - var batch = PIXI.WebGLRenderer.getBatch(); - var index = this.batchs.indexOf( currentBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); + this.renderSpecial(endBatch, projection); } } -PIXI.WebGLRenderGroup.prototype.addDisplayObject = function(displayObject) +/** + * Renders a specific renderable + * + * @method renderSpecial + * @param renderable {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { - // add a child to the render group.. + 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) + { + /* + * for now only masks are supported.. + */ + + var gl = PIXI.gl; + + 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); + + // we know this is a render texture so enable alpha too.. + 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); + } + } +} + +/** + * 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) + { + 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) + { + 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); - - // DONT htink this is needed? - // displayObject.batch = null; - displayObject.__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 = 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) +} - //displayObject.cacheVisible = true; - if(!displayObject.renderable)return; +/** + * 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 - //displayObject.__inWebGL = true; + var previousSprite = previousObject; + var nextSprite = nextObject; - var previousSprite = this.getPreviousRenderable(displayObject); - var nextSprite = this.getNextRenderable(displayObject); - - /* * so now we have the next renderable and the previous renderable * */ - if(displayObject instanceof PIXI.Sprite) { var previousBatch var nextBatch - //console.log( previousSprite) if(previousSprite instanceof PIXI.Sprite) { previousBatch = previousSprite.batch; @@ -3926,6 +5840,7 @@ else { // TODO re-word! + nextBatch = nextSprite; } } @@ -3948,40 +5863,101 @@ { this.batchs.push(batch); } - + + return; } else if(displayObject instanceof PIXI.TilingSprite) { + // add to a batch!! this.initTilingSprite(displayObject); - this.batchs.push(displayObject); + // this.batchs.push(displayObject); } else if(displayObject instanceof PIXI.Strip) { // add to a batch!! this.initStrip(displayObject); - this.batchs.push(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); } - // if its somthing else... then custom codes! - this.batchUpdate = true; + this.insertAfter(displayObject, previousSprite); + + // insert and SPLIT! + } -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) +/** + * 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) { - // TODO - this can be faster - but not as important right now - - this.addDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) + if(displayObject instanceof PIXI.Sprite) { - this.addDisplayObjectAndChildren(children[i]); - }; + 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); + } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObject = function(displayObject) +/** + * 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 // @@ -3989,10 +5965,7 @@ // add a child from the render group.. // remove it and all its children! //displayObject.cacheVisible = false;//displayObject.visible; - displayObject.__renderGroup = null; - - if(!displayObject.renderable)return; - + /* * removing is a lot quicker.. * @@ -4050,113 +6023,16 @@ } } - this.batchs.splice(index, 1); if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); } } -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - // TODO - this can be faster - but not as important right now - if(displayObject.__renderGroup != this)return; - - this.removeDisplayObject(displayObject); - var children = displayObject.children; - - for (var i=0; i < children.length; i++) - { - this.removeDisplayObjectAndChildren(children[i]); - }; -} - /** - * @private - */ - -PIXI.WebGLRenderGroup.prototype.getNextRenderable = function(displayObject) -{ - /* - * 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 - * - * These look a lot scarier than the actually are... - */ - - var nextSprite = displayObject; - do - { - // moving forward! - // if it has no children.. - if(nextSprite.children.length == 0) - { - //maynot have a parent - if(!nextSprite.parent)return null; - - // go along to the parent.. - while(nextSprite.childIndex == nextSprite.parent.children.length-1) - { - nextSprite = nextSprite.parent; - //console.log(">" + nextSprite); -// console.log(">-" + this.root); - if(nextSprite == this.root || !nextSprite.parent)//displayObject.stage) - { - nextSprite = null - break; - } - } - - if(nextSprite)nextSprite = nextSprite.parent.children[nextSprite.childIndex+1]; - } - else - { - nextSprite = nextSprite.children[0]; - } - - if(!nextSprite)break; - } - while(!nextSprite.renderable || !nextSprite.__renderGroup) - - return nextSprite; -} - -PIXI.WebGLRenderGroup.prototype.getPreviousRenderable = function(displayObject) -{ - /* - * LOOK FOR THE PREVIOUS SPRITE - * 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 previousSprite = displayObject; - do - { - if(previousSprite.childIndex == 0) - { - previousSprite = previousSprite.parent; - if(!previousSprite)return null; - } - else - { - - previousSprite = previousSprite.parent.children[previousSprite.childIndex-1]; - // what if the bloop has children??? - while(previousSprite.children.length != 0) - { - // keep diggin till we get to the last child - previousSprite = previousSprite.children[previousSprite.children.length-1]; - } - } - - if(previousSprite == this.root)break; - } - while(!previousSprite.renderable || !previousSprite.__renderGroup); - - return previousSprite; -} - -/** + * Initializes a tiling sprite + * + * @method initTilingSprite + * @param sprite {TilingSprite} The tiling sprite to initialize * @private */ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) @@ -4179,7 +6055,6 @@ sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - sprite._vertexBuffer = gl.createBuffer(); sprite._indexBuffer = gl.createBuffer(); sprite._uvBuffer = gl.createBuffer(); @@ -4213,19 +6088,35 @@ } /** + * Renders a Strip + * + * @method renderStrip + * @param strip {Strip} The strip to render + * @param projection {Object} * @private */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projectionMatrix) +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 mat4Real = PIXI.mat3.toMat4(strip.worldTransform); + //PIXI.mat4.transpose(mat4Real); + //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - + + gl.useProgram(PIXI.stripShaderProgram); + + 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); + +/* if(strip.blendMode == PIXI.blendModes.NORMAL) { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); @@ -4234,6 +6125,8 @@ { gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } + */ + if(!strip.dirty) { @@ -4254,8 +6147,6 @@ // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - - } else { @@ -4281,15 +6172,19 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP) + //console.log(gl.TRIANGLE_STRIP); + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, projectionMatrix); - + gl.useProgram(PIXI.shaderProgram); } - /** + * Renders a TilingSprite + * + * @method renderTilingSprite + * @param sprite {TilingSprite} The tiling sprite to render + * @param projectionMatrix {Object} * @private */ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) @@ -4324,9 +6219,11 @@ 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) @@ -4354,7 +6251,6 @@ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4363,65 +6259,66 @@ /** * 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 {Number} the width of the canvas view - * @default 0 - * @param height {Number} the height of the canvas view - * @default 0 + * @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 {Boolean} the transparency of the render view, default false - * @default false - * + * @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; - - this.refresh = true; - + /** * The canvas element that the everything is drawn to + * * @property view * @type Canvas */ - this.view = view || document.createElement( 'canvas' ); - - // 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; - + 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.constructor = PIXI.CanvasRenderer; +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the stage to its canvas view + * * @method render * @param stage {Stage} the Stage element to be rendered */ @@ -4462,12 +6359,16 @@ { PIXI.Texture.frameUpdates = []; } + + } /** * resizes the canvas view to the specified width and height - * @param the new width of the canvas view - * @param the new height of the canvas view + * + * @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) { @@ -4479,52 +6380,51 @@ } /** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render * @private */ - PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) { - var transform = displayObject.worldTransform; + // no loger recurrsive! + var transform; var context = this.context; - //context.globalCompositeOperation = "source-over" - var blit = false; - if(!displayObject.visible)return; - - if(displayObject instanceof PIXI.Sprite) + context.globalCompositeOperation = 'source-over'; + + // one the display object hits this. we can break the loop + var testObject = displayObject.last._iNext; + displayObject = displayObject.first; + + do { - var frame = displayObject.texture.frame; + transform = displayObject.worldTransform; - if(frame) + if(!displayObject.visible) { - context.globalAlpha = displayObject.worldAlpha; + displayObject = displayObject.last._iNext; + continue; + } + + if(!displayObject.renderable) + { + displayObject = displayObject._iNext; + continue; + } + + if(displayObject instanceof PIXI.Sprite) + { + + var frame = displayObject.texture.frame; - // BLITZ!!! - /* - * if the rotation is 0 then we can blitz it - * meaning we dont need to do a transform and also we - * can round to the nearest round number for a little extra speed! - */ - /*if(displayObject.rotation == 0) + if(frame) { - if(!blit)this.context.setTransform(1,0,0,1,0,0); - blit = true; - context.drawImage(displayObject.texture.baseTexture.image, - frame.x, - frame.y, - frame.width, - frame.height, - (transform[2]+ ((displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width) * transform[0]), - (transform[5]+ ((displayObject.anchor.y - displayObject.texture.trim.y) * -frame.height)* transform[4]), - (displayObject.width * transform[0]), - (displayObject.height * transform[4])); + context.globalAlpha = displayObject.worldAlpha; - } - else - {*/ - // blit = false; context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, @@ -4532,42 +6432,69 @@ frame.height, (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, - // (displayObject.anchor.x - displayObject.texture.trim.x) * -frame.width, - // (displayObject.anchor.y - displayObject.texture.trim.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); - } - - // render! - if(displayObject.children) - { - for (var i=0; i < displayObject.children.length; i++) + } + } + else if(displayObject instanceof PIXI.Strip) { - this.renderDisplayObject(displayObject.children[i]); + 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(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(); + } + } + // count++ + displayObject = displayObject._iNext; + + } + while(displayObject != testObject) + - this.context.setTransform(1,0,0,1,0,0); } /** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) @@ -4595,20 +6522,24 @@ }; -// context.globalCompositeOperation = 'lighter'; context.fillStyle = "#FF0000"; context.fill(); context.closePath(); - //context.globalCompositeOperation = 'source-over'; } /** + * 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(); @@ -4629,15 +6560,17 @@ 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; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -4664,8 +6597,6 @@ context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -4689,17 +6620,479 @@ context.restore(); }; -// context.globalCompositeOperation = 'source-over'; +} + +/** + * @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) + { + + // TODO - need to be Undefined! + if(data.fillColor) + { + 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/ */ @@ -4768,8 +7161,8 @@ } // constructor -PIXI.Strip.constructor = PIXI.Strip; PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.prototype.setTexture = function(texture) { @@ -4789,7 +7182,7 @@ } // some helper functions.. - + /** * @author Mat Groves http://matgroves.com/ */ @@ -4821,8 +7214,8 @@ // constructor -PIXI.Rope.constructor = PIXI.Rope; PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; PIXI.Rope.prototype.refresh = function() { @@ -4965,13 +7358,14 @@ - + /** * @author Mat Groves http://matgroves.com/ */ /** * A tiling sprite is a fast way of rendering a tiling image + * * @class TilingSprite * @extends DisplayObjectContainer * @constructor @@ -4982,32 +7376,62 @@ 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; - this.renderable = true; - + /** * 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.constructor = PIXI.TilingSprite; 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 @@ -5018,117 +7442,143 @@ 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 - * Also due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * * @class Spine - * @constructor * @extends DisplayObjectContainer - * @param {String} url the url of the spine anim file to be used + * @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.constructor = PIXI.Spine; -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); + 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(); - - 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 @@ -5244,7 +7694,7 @@ 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) { @@ -5475,6 +7925,7 @@ 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; } @@ -5499,14 +7950,12 @@ 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; } @@ -5544,6 +7993,7 @@ 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. @@ -5593,7 +8043,7 @@ 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; @@ -5610,11 +8060,6 @@ 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)); } }; @@ -5786,11 +8231,9 @@ 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; } @@ -5872,7 +8315,6 @@ offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -5896,6 +8338,7 @@ 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; @@ -5908,7 +8351,7 @@ }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -5948,7 +8391,7 @@ this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -6094,16 +8537,9 @@ 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; @@ -6112,10 +8548,19 @@ 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; @@ -6166,7 +8611,7 @@ } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -6209,8 +8654,8 @@ 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 + ")"; } @@ -6470,14 +8915,15 @@ PIXI.AnimCache = {}; spine.Bone.yDown = true; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * Need to finalize this a bit more but works! Its in but will be working on this feature properly next..:) + * This object is one that will allow you to specify custom rendering functions based on render type + * * @class CustomRenderable * @extends DisplayObject * @constructor @@ -6489,28 +8935,44 @@ } // constructor -PIXI.CustomRenderable.constructor = PIXI.CustomRenderable; 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 */ @@ -6521,45 +8983,54 @@ /** * A texture stores the information that represents an image. All textures have a base texture + * * @class BaseTexture - * @extends EventTarget + * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) */ PIXI.BaseTexture = function(source) { PIXI.EventTarget.call( this ); - - /* - * The url of the texture - * @property imageUrl - * @type String - */ - //this.imageUrl = source.src; - + /** - * [read only] The width of the base texture set when the image has loaded + * [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 + * [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//new Image(); - + this.source = source; + if(!source)return; - - if(this.source instanceof Image) + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) { if(this.source.complete) { @@ -6594,13 +9065,17 @@ PIXI.texturesToUpdate.push(this); } - + this._powerOf2 = false; - } -PIXI.BaseTexture.constructor = PIXI.BaseTexture; +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +/** + * Destroys this base texture + * + * @method destroy + */ PIXI.BaseTexture.prototype.destroy = function() { if(this.source instanceof Image) @@ -6612,9 +9087,9 @@ } /** - * * 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 @@ -6639,7 +9114,7 @@ return baseTexture; } - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6648,46 +9123,54 @@ 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 + * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param baseTexture {BaseTexture} - * @param frmae {Rectangle} + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frmae {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); } - - this.trim = new PIXI.Point(); 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 + * @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); @@ -6702,21 +9185,34 @@ } } -PIXI.Texture.constructor = PIXI.Texture; +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(); @@ -6724,33 +9220,35 @@ /** * Specifies the rectangle region of the baseTexture + * * @method setFrame - * @param frame {Rectangle} + * @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) @@ -6767,9 +9265,10 @@ } /** - * * 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 @@ -6782,9 +9281,9 @@ } /** - * * 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 @@ -6798,8 +9297,8 @@ /** - * - * Adds a texture to the textureCache. + * Adds a texture to the textureCache. + * * @static * @method addTextureToCache * @param texture {Texture} @@ -6811,8 +9310,8 @@ } /** - * * Remove a texture from the textureCache. + * * @static * @method removeTextureFromCache * @param id {String} the id of the texture to be removed @@ -6828,7 +9327,7 @@ // this is more for webGL.. it contains updated frames.. PIXI.Texture.frameUpdates = []; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6858,20 +9357,20 @@ @class RenderTexture @extends Texture @constructor - @param width {Number} - @param height {Number} - **/ + @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(); @@ -6882,19 +9381,25 @@ } } -PIXI.RenderTexture.constructor = PIXI.RenderTexture; 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; @@ -6902,142 +9407,226 @@ 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.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; + 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 render - * @param displayObject {DisplayObject} + * + * @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, clear) +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) +PIXI.AssetLoader = function(assetURLs, crossorigin) { PIXI.EventTarget.call(this); - + /** * The array of asset URLs that are going to be loaded + * * @property assetURLs - * @type Array + * @type Array */ this.assetURLs = assetURLs; - this.crossorigin = false; + /** + * 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, @@ -7053,20 +9642,22 @@ }; /** -Fired when an item has loaded -@event onProgress -**/ + * Fired when an item has loaded + * @event onProgress + */ /** -Fired when all the assets have loaded -@event onComplete -**/ + * Fired when all the assets have loaded + * @event onComplete + */ // constructor -PIXI.AssetLoader.constructor = PIXI.AssetLoader; +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * This will begin loading the assets sequentially + * Starts loading the assets sequentially + * + * @method load */ PIXI.AssetLoader.prototype.load = function() { @@ -7095,6 +9686,8 @@ /** * Invoked after each file is loaded + * + * @method onAssetLoaded * @private */ PIXI.AssetLoader.prototype.onAssetLoaded = function() @@ -7110,7 +9703,7 @@ } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7119,27 +9712,59 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @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; - this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * 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.constructor = PIXI.JsonLoader; +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; /** - * This will begin loading the JSON file + * Loads the JSON data + * + * @method load */ PIXI.JsonLoader.prototype.load = function () { this.ajaxRequest = new AjaxRequest(); @@ -7155,6 +9780,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { @@ -7208,11 +9835,9 @@ { this.onLoaded(); } - - - - - } else { + } + else + { this.onError(); } } @@ -7220,6 +9845,8 @@ /** * Invoke when json file loaded + * + * @method onLoaded * @private */ PIXI.JsonLoader.prototype.onLoaded = function () { @@ -7232,6 +9859,8 @@ /** * Invoke when error occured + * + * @method onError * @private */ PIXI.JsonLoader.prototype.onError = function () { @@ -7239,7 +9868,7 @@ type: "error", content: this }); -}; +}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7252,11 +9881,12 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @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) { @@ -7266,18 +9896,56 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; - this.frames = {}; + + /** + * 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.constructor = PIXI.SpriteSheetLoader; +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; /** * This will begin loading the JSON file + * + * @method load */ PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; @@ -7291,6 +9959,8 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { @@ -7326,6 +9996,8 @@ }; /** * Invoke when all files are loaded (json and texture) + * + * @method onLoaded * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { @@ -7334,7 +10006,7 @@ content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7343,23 +10015,33 @@ * 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 - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url The url of the image - * @param {Boolean} crossorigin + * @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); }; // constructor -PIXI.ImageLoader.constructor = PIXI.ImageLoader; +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; /** * Loads image or takes it from cache + * + * @method load */ PIXI.ImageLoader.prototype.load = function() { @@ -7379,13 +10061,15 @@ /** * 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}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7395,13 +10079,13 @@ * To generate the data you can use http://www.angelcode.com/products/bmfont/ * This loader will also load the image file as the data. * When loaded this class will dispatch a "loaded" event + * * @class BitmapFontLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.BitmapFontLoader = function(url, crossorigin) { /* @@ -7410,17 +10094,48 @@ * 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; - this.baseUrl = url.replace(/[^\/]*$/, ""); - this.texture = null; + + /** + * 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] The texture of the bitmap font + * + * @property baseUrl + * @type String + */ + this.texture = null; }; // constructor -PIXI.BitmapFontLoader.constructor = PIXI.BitmapFontLoader; +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; /** - * This will begin loading the JSON file + * Loads the XML font data + * + * @method load */ PIXI.BitmapFontLoader.prototype.load = function() { @@ -7437,7 +10152,9 @@ }; /** - * Invoked when XML file is loaded + * Invoked when XML file is loaded, parses the data + * + * @method onXMLLoaded * @private */ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() @@ -7508,13 +10225,15 @@ /** * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded * @private */ PIXI.BitmapFontLoader.prototype.onLoaded = function() { this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi @@ -7531,32 +10250,50 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data * When loaded this class will dispatch a "loaded" event + * * @class Spine + * @uses EventTarget * @constructor - * @extends EventTarget - * @param {String} url the url of the sprite sheet JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpineLoader = 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] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ this.loaded = false; } -PIXI.SpineLoader.constructor = PIXI.SpineLoader; +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; -PIXI.SpineLoader.prototype.load = function() -{ - new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener("loaded", function (event) { - scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - +/** + * Loads the JSON data + * + * @method load + */ PIXI.SpineLoader.prototype.load = function () { var scope = this; @@ -7570,12 +10307,12 @@ /** * Invoke when JSON file is loaded + * + * @method onJSONLoaded * @private */ PIXI.SpineLoader.prototype.onJSONLoaded = function (event) { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); PIXI.AnimCache[this.url] = skeletonData; @@ -7583,15 +10320,18 @@ this.onLoaded(); }; - - -PIXI.SpineLoader.prototype.onLoaded = function() -{ +/** + * Invoke when JSON file is loaded + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { this.loaded = true; this.dispatchEvent({type: "loaded", content: this}); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */