mirror of
https://github.com/photonstorm/phaser
synced 2024-11-27 23:20:59 +00:00
9708 lines
No EOL
259 KiB
JavaScript
9708 lines
No EOL
259 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @copyright 2016 Photon Storm Ltd.
|
|
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
|
*
|
|
* @overview
|
|
*
|
|
* Phaser - http://phaser.io
|
|
*
|
|
* v3.0.0 "Shadow Coast" - Built: Wed Sep 21 2016 23:21:17
|
|
*
|
|
* By Richard Davey http://www.photonstorm.com @photonstorm
|
|
*
|
|
* Phaser is a fun, free and fast 2D game framework for making HTML5 games
|
|
* for desktop and mobile web browsers, supporting Canvas and WebGL rendering.
|
|
*
|
|
* Phaser uses Pixi.js for rendering, created by Mat Groves http://matgroves.com @Doormat23
|
|
* Phaser uses p2.js for full-body physics, created by Stefan Hedman https://github.com/schteppe/p2.js @schteppe
|
|
* Phaser contains a port of N+ Physics, converted by Richard Davey, original by http://www.metanetsoftware.com
|
|
*
|
|
* Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from which both Phaser and my love of framework development originate.
|
|
*
|
|
* Follow development at http://phaser.io and on our forum
|
|
*
|
|
* "If you want your children to be intelligent, read them fairy tales."
|
|
* "If you want them to be more intelligent, read them more fairy tales."
|
|
* -- Albert Einstein
|
|
*/
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var root = this;
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* The [pixi.js](http://www.pixijs.com/) module/namespace.
|
|
*
|
|
* @module PIXI
|
|
*/
|
|
|
|
/**
|
|
* Namespace-class for [pixi.js](http://www.pixijs.com/).
|
|
*
|
|
* Contains assorted static properties and enumerations.
|
|
*
|
|
* @class PIXI
|
|
* @static
|
|
*/
|
|
var PIXI = PIXI || {};
|
|
|
|
/**
|
|
* A reference to the Phaser Game instance that owns this Pixi renderer.
|
|
* @property {Phaser.Game} game
|
|
* @static
|
|
*/
|
|
PIXI.game = null;
|
|
|
|
/**
|
|
* @property {Number} WEBGL_RENDERER
|
|
* @protected
|
|
* @static
|
|
*/
|
|
PIXI.WEBGL_RENDERER = 0;
|
|
|
|
/**
|
|
* @property {Number} CANVAS_RENDERER
|
|
* @protected
|
|
* @static
|
|
*/
|
|
PIXI.CANVAS_RENDERER = 1;
|
|
|
|
/**
|
|
* Version of pixi that is loaded.
|
|
* @property {String} VERSION
|
|
* @static
|
|
*/
|
|
PIXI.VERSION = "v2.2.9";
|
|
|
|
// used to create uids for various pixi objects.
|
|
PIXI._UID = 0;
|
|
|
|
if (typeof(Float32Array) != 'undefined')
|
|
{
|
|
PIXI.Float32Array = Float32Array;
|
|
PIXI.Uint16Array = Uint16Array;
|
|
|
|
// Uint32Array and ArrayBuffer only used by WebGL renderer
|
|
// We can suppose that if WebGL is supported then typed arrays are supported too
|
|
// as they predate WebGL support for all browsers:
|
|
// see typed arrays support: http://caniuse.com/#search=TypedArrays
|
|
// see WebGL support: http://caniuse.com/#search=WebGL
|
|
PIXI.Uint32Array = Uint32Array;
|
|
PIXI.ArrayBuffer = ArrayBuffer;
|
|
}
|
|
else
|
|
{
|
|
PIXI.Float32Array = Array;
|
|
PIXI.Uint16Array = Array;
|
|
}
|
|
|
|
/**
|
|
* @property {Number} PI_2
|
|
* @static
|
|
*/
|
|
PIXI.PI_2 = Math.PI * 2;
|
|
|
|
/**
|
|
* @property {Number} RAD_TO_DEG
|
|
* @static
|
|
*/
|
|
PIXI.RAD_TO_DEG = 180 / Math.PI;
|
|
|
|
/**
|
|
* @property {Number} DEG_TO_RAD
|
|
* @static
|
|
*/
|
|
PIXI.DEG_TO_RAD = Math.PI / 180;
|
|
|
|
/**
|
|
* @property {String} RETINA_PREFIX
|
|
* @protected
|
|
* @static
|
|
*/
|
|
PIXI.RETINA_PREFIX = "@2x";
|
|
|
|
/**
|
|
* The default render options if none are supplied to
|
|
* {{#crossLink "WebGLRenderer"}}{{/crossLink}} or {{#crossLink "CanvasRenderer"}}{{/crossLink}}.
|
|
*
|
|
* @property {Object} defaultRenderOptions
|
|
* @property {Object} defaultRenderOptions.view=null
|
|
* @property {Boolean} defaultRenderOptions.transparent=false
|
|
* @property {Boolean} defaultRenderOptions.antialias=false
|
|
* @property {Boolean} defaultRenderOptions.preserveDrawingBuffer=false
|
|
* @property {Number} defaultRenderOptions.resolution=1
|
|
* @property {Boolean} defaultRenderOptions.clearBeforeRender=true
|
|
* @property {Boolean} defaultRenderOptions.autoResize=false
|
|
* @static
|
|
PIXI.defaultRenderOptions = {
|
|
view: null,
|
|
transparent: false,
|
|
antialias: false,
|
|
preserveDrawingBuffer: false,
|
|
resolution: 1,
|
|
clearBeforeRender: true,
|
|
autoResize: false
|
|
};
|
|
*/
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com @Doormat23
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
|
*/
|
|
|
|
/**
|
|
* The base class for all objects that are rendered. Contains properties for position, scaling,
|
|
* rotation, masks and cache handling.
|
|
*
|
|
* This is an abstract class and should not be used on its own, rather it should be extended.
|
|
*
|
|
* It is used internally by the likes of PIXI.Sprite.
|
|
*
|
|
* @class PIXI.DisplayObject
|
|
* @constructor
|
|
*/
|
|
PIXI.DisplayObject = function () {
|
|
|
|
/**
|
|
* The coordinates, in pixels, of this DisplayObject, relative to its parent container.
|
|
*
|
|
* The value of this property does not reflect any positioning happening further up the display list.
|
|
* To obtain that value please see the `worldPosition` property.
|
|
*
|
|
* @property {PIXI.Point} position
|
|
* @default
|
|
*/
|
|
this.position = new PIXI.Point(0, 0);
|
|
|
|
/**
|
|
* The scale of this DisplayObject. A scale of 1:1 represents the DisplayObject
|
|
* at its default size. A value of 0.5 would scale this DisplayObject by half, and so on.
|
|
*
|
|
* The value of this property does not reflect any scaling happening further up the display list.
|
|
* To obtain that value please see the `worldScale` property.
|
|
*
|
|
* @property {PIXI.Point} scale
|
|
* @default
|
|
*/
|
|
this.scale = new PIXI.Point(1, 1);
|
|
|
|
/**
|
|
* The pivot point of this DisplayObject that it rotates around. The values are expressed
|
|
* in pixel values.
|
|
* @property {PIXI.Point} pivot
|
|
* @default
|
|
*/
|
|
this.pivot = new PIXI.Point(0, 0);
|
|
|
|
/**
|
|
* The rotation of this DisplayObject. The value is given, and expressed, in radians, and is based on
|
|
* a right-handed orientation.
|
|
*
|
|
* The value of this property does not reflect any rotation happening further up the display list.
|
|
* To obtain that value please see the `worldRotation` property.
|
|
*
|
|
* @property {number} rotation
|
|
* @default
|
|
*/
|
|
this.rotation = 0;
|
|
|
|
/**
|
|
* The alpha value of this DisplayObject. A value of 1 is fully opaque. A value of 0 is transparent.
|
|
* Please note that an object with an alpha value of 0 is skipped during the render pass.
|
|
*
|
|
* The value of this property does not reflect any alpha values set further up the display list.
|
|
* To obtain that value please see the `worldAlpha` property.
|
|
*
|
|
* @property {number} alpha
|
|
* @default
|
|
*/
|
|
this.alpha = 1;
|
|
|
|
/**
|
|
* The visibility of this DisplayObject. A value of `false` makes the object invisible.
|
|
* A value of `true` makes it visible. Please note that an object with a visible value of
|
|
* `false` is skipped during the render pass. Equally a DisplayObject with visible false will
|
|
* not render any of its children.
|
|
*
|
|
* The value of this property does not reflect any visible values set further up the display list.
|
|
* To obtain that value please see the `worldVisible` property.
|
|
*
|
|
* @property {boolean} visible
|
|
* @default
|
|
*/
|
|
this.visible = true;
|
|
|
|
/**
|
|
* This is the defined area that will pick up mouse / touch events. It is null by default.
|
|
* Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children)
|
|
*
|
|
* @property hitArea
|
|
* @type Rectangle|Circle|Ellipse|Polygon
|
|
*/
|
|
this.hitArea = null;
|
|
|
|
/**
|
|
* Should this DisplayObject be rendered by the renderer? An object with a renderable value of
|
|
* `false` is skipped during the render pass.
|
|
*
|
|
* @property {boolean} renderable
|
|
* @default
|
|
*/
|
|
this.renderable = false;
|
|
|
|
/**
|
|
* The parent DisplayObjectContainer that this DisplayObject is a child of.
|
|
* All DisplayObjects must belong to a parent in order to be rendered.
|
|
* The root parent is the Stage object. This property is set automatically when the
|
|
* DisplayObject is added to, or removed from, a DisplayObjectContainer.
|
|
*
|
|
* @property {PIXI.DisplayObjectContainer} parent
|
|
* @default
|
|
* @readOnly
|
|
*/
|
|
this.parent = null;
|
|
|
|
/**
|
|
* The multiplied alpha value of this DisplayObject. A value of 1 is fully opaque. A value of 0 is transparent.
|
|
* This value is the calculated total, based on the alpha values of all parents of this DisplayObjects
|
|
* in the display list.
|
|
*
|
|
* To obtain, and set, the local alpha value, see the `alpha` property.
|
|
*
|
|
* Note: This property is only updated at the end of the `updateTransform` call, once per render. Until
|
|
* that happens this property will contain values based on the previous frame. Be mindful of this if
|
|
* accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
|
|
*
|
|
* @property {number} worldAlpha
|
|
* @readOnly
|
|
*/
|
|
this.worldAlpha = 1;
|
|
|
|
/**
|
|
* The current transform of this DisplayObject.
|
|
*
|
|
* This property contains the calculated total, based on the transforms of all parents of this
|
|
* DisplayObject in the display list.
|
|
*
|
|
* Note: This property is only updated at the end of the `updateTransform` call, once per render. Until
|
|
* that happens this property will contain values based on the previous frame. Be mindful of this if
|
|
* accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
|
|
*
|
|
* @property {PIXI.Matrix} worldTransform
|
|
* @readOnly
|
|
*/
|
|
this.worldTransform = new PIXI.Matrix();
|
|
|
|
/**
|
|
* The coordinates, in pixels, of this DisplayObject within the world.
|
|
*
|
|
* This property contains the calculated total, based on the positions of all parents of this
|
|
* DisplayObject in the display list.
|
|
*
|
|
* Note: This property is only updated at the end of the `updateTransform` call, once per render. Until
|
|
* that happens this property will contain values based on the previous frame. Be mindful of this if
|
|
* accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
|
|
*
|
|
* @property {PIXI.Point} worldPosition
|
|
* @readOnly
|
|
*/
|
|
this.worldPosition = new PIXI.Point(0, 0);
|
|
|
|
/**
|
|
* The global scale of this DisplayObject.
|
|
*
|
|
* This property contains the calculated total, based on the scales of all parents of this
|
|
* DisplayObject in the display list.
|
|
*
|
|
* Note: This property is only updated at the end of the `updateTransform` call, once per render. Until
|
|
* that happens this property will contain values based on the previous frame. Be mindful of this if
|
|
* accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
|
|
*
|
|
* @property {PIXI.Point} worldScale
|
|
* @readOnly
|
|
*/
|
|
this.worldScale = new PIXI.Point(1, 1);
|
|
|
|
/**
|
|
* The rotation, in radians, of this DisplayObject.
|
|
*
|
|
* This property contains the calculated total, based on the rotations of all parents of this
|
|
* DisplayObject in the display list.
|
|
*
|
|
* Note: This property is only updated at the end of the `updateTransform` call, once per render. Until
|
|
* that happens this property will contain values based on the previous frame. Be mindful of this if
|
|
* accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
|
|
*
|
|
* @property {number} worldRotation
|
|
* @readOnly
|
|
*/
|
|
this.worldRotation = 0;
|
|
|
|
/**
|
|
* The rectangular area used by filters when rendering a shader for this DisplayObject.
|
|
*
|
|
* @property {PIXI.Rectangle} filterArea
|
|
* @type Rectangle
|
|
* @default
|
|
*/
|
|
this.filterArea = null;
|
|
|
|
/**
|
|
* @property {number} _sr - Cached rotation value.
|
|
* @private
|
|
*/
|
|
this._sr = 0;
|
|
|
|
/**
|
|
* @property {number} _cr - Cached rotation value.
|
|
* @private
|
|
*/
|
|
this._cr = 1;
|
|
|
|
/**
|
|
* @property {PIXI.Rectangle} _bounds - The cached bounds of this object.
|
|
* @private
|
|
*/
|
|
this._bounds = new PIXI.Rectangle(0, 0, 0, 0);
|
|
|
|
/**
|
|
* @property {PIXI.Rectangle} _currentBounds - The most recently calculated bounds of this object.
|
|
* @private
|
|
*/
|
|
this._currentBounds = null;
|
|
|
|
/**
|
|
* @property {PIXI.Rectangle} _mask - The cached mask of this object.
|
|
* @private
|
|
*/
|
|
this._mask = null;
|
|
|
|
/**
|
|
* @property {boolean} _cacheAsBitmap - Internal cache as bitmap flag.
|
|
* @private
|
|
*/
|
|
this._cacheAsBitmap = false;
|
|
|
|
/**
|
|
* @property {boolean} _cacheIsDirty - Internal dirty cache flag.
|
|
* @private
|
|
*/
|
|
this._cacheIsDirty = false;
|
|
|
|
};
|
|
|
|
PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject;
|
|
|
|
PIXI.DisplayObject.prototype = {
|
|
|
|
/**
|
|
* Destroy this DisplayObject.
|
|
*
|
|
* Removes any cached sprites, sets renderable flag to false, and nulls filters, bounds and mask.
|
|
*
|
|
* Also iteratively calls `destroy` on any children.
|
|
*
|
|
* @method PIXI.DisplayObject#destroy
|
|
*/
|
|
destroy: function () {
|
|
|
|
if (this.children)
|
|
{
|
|
var i = this.children.length;
|
|
|
|
while (i--)
|
|
{
|
|
this.children[i].destroy();
|
|
}
|
|
|
|
this.children = [];
|
|
}
|
|
|
|
this.hitArea = null;
|
|
this.parent = null;
|
|
this.worldTransform = null;
|
|
this.filterArea = null;
|
|
this.renderable = false;
|
|
|
|
this._bounds = null;
|
|
this._currentBounds = null;
|
|
this._mask = null;
|
|
|
|
this._destroyCachedSprite();
|
|
|
|
},
|
|
|
|
/*
|
|
* Updates the transform matrix this DisplayObject uses for rendering.
|
|
*
|
|
* If the object has no parent, and no parent parameter is provided, it will default to
|
|
* Phaser.Game.World as the parent transform to use. If that is unavailable the transform fails to take place.
|
|
*
|
|
* The `parent` parameter has priority over the actual parent. Use it as a parent override.
|
|
* Setting it does **not** change the actual parent of this DisplayObject.
|
|
*
|
|
* Calling this method updates the `worldTransform`, `worldAlpha`, `worldPosition`, `worldScale`
|
|
* and `worldRotation` properties.
|
|
*
|
|
* If a `transformCallback` has been specified, it is called at the end of this method, and is passed
|
|
* the new, updated, worldTransform property, along with the parent transform used.
|
|
*
|
|
* @method PIXI.DisplayObject#updateTransform
|
|
* @param {PIXI.DisplayObjectContainer} [parent] - Optional parent to calculate this DisplayObjects transform from.
|
|
* @return {PIXI.DisplayObject} - A reference to this DisplayObject.
|
|
*/
|
|
updateTransform: function (parent) {
|
|
|
|
if (!parent && !this.parent && !this.game)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
var p = this.parent;
|
|
|
|
if (parent)
|
|
{
|
|
p = parent;
|
|
}
|
|
else if (!this.parent)
|
|
{
|
|
p = this.game.world;
|
|
}
|
|
|
|
// create some matrix refs for easy access
|
|
var pt = p.worldTransform;
|
|
var wt = this.worldTransform;
|
|
|
|
// temporary matrix variables
|
|
var a, b, c, d, tx, ty;
|
|
|
|
// so if rotation is between 0 then we can simplify the multiplication process..
|
|
if (this.rotation % PIXI.PI_2)
|
|
{
|
|
// check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes
|
|
if (this.rotation !== this.rotationCache)
|
|
{
|
|
this.rotationCache = this.rotation;
|
|
this._sr = Math.sin(this.rotation);
|
|
this._cr = Math.cos(this.rotation);
|
|
}
|
|
|
|
// get the matrix values of the displayobject based on its transform properties..
|
|
a = this._cr * this.scale.x;
|
|
b = this._sr * this.scale.x;
|
|
c = -this._sr * this.scale.y;
|
|
d = this._cr * this.scale.y;
|
|
tx = this.position.x;
|
|
ty = this.position.y;
|
|
|
|
// check for pivot.. not often used so geared towards that fact!
|
|
if (this.pivot.x || this.pivot.y)
|
|
{
|
|
tx -= this.pivot.x * a + this.pivot.y * c;
|
|
ty -= this.pivot.x * b + this.pivot.y * d;
|
|
}
|
|
|
|
// concat the parent matrix with the objects transform.
|
|
wt.a = a * pt.a + b * pt.c;
|
|
wt.b = a * pt.b + b * pt.d;
|
|
wt.c = c * pt.a + d * pt.c;
|
|
wt.d = c * pt.b + d * pt.d;
|
|
wt.tx = tx * pt.a + ty * pt.c + pt.tx;
|
|
wt.ty = tx * pt.b + ty * pt.d + pt.ty;
|
|
}
|
|
else
|
|
{
|
|
// lets do the fast version as we know there is no rotation..
|
|
a = this.scale.x;
|
|
d = this.scale.y;
|
|
|
|
tx = this.position.x - this.pivot.x * a;
|
|
ty = this.position.y - this.pivot.y * d;
|
|
|
|
wt.a = a * pt.a;
|
|
wt.b = a * pt.b;
|
|
wt.c = d * pt.c;
|
|
wt.d = d * pt.d;
|
|
wt.tx = tx * pt.a + ty * pt.c + pt.tx;
|
|
wt.ty = tx * pt.b + ty * pt.d + pt.ty;
|
|
}
|
|
|
|
// Set the World values
|
|
this.worldAlpha = this.alpha * p.worldAlpha;
|
|
this.worldPosition.set(wt.tx, wt.ty);
|
|
this.worldScale.set(this.scale.x * Math.sqrt(wt.a * wt.a + wt.c * wt.c), this.scale.y * Math.sqrt(wt.b * wt.b + wt.d * wt.d));
|
|
this.worldRotation = Math.atan2(-wt.c, wt.d);
|
|
|
|
// reset the bounds each time this is called!
|
|
this._currentBounds = null;
|
|
|
|
// Custom callback?
|
|
if (this.transformCallback)
|
|
{
|
|
this.transformCallback.call(this.transformCallbackContext, wt, pt);
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
/**
|
|
* To be overridden by classes that require it.
|
|
*
|
|
* @method PIXI.DisplayObject#preUpdate
|
|
*/
|
|
preUpdate: function () {
|
|
|
|
},
|
|
|
|
/**
|
|
* Generates a RenderTexture based on this DisplayObject, which can they be used to texture other Sprites.
|
|
* This can be useful if your DisplayObject is static, or complicated, and needs to be reused multiple times.
|
|
*
|
|
* Please note that no garbage collection takes place on old textures. It is up to you to destroy old textures,
|
|
* and references to them, so they don't linger in memory.
|
|
*
|
|
* @method PIXI.DisplayObject#generateTexture
|
|
* @param {number} [resolution=1] - The resolution of the texture being generated.
|
|
* @param {number} [scaleMode=PIXI.scaleModes.DEFAULT] - See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values.
|
|
* @param {PIXI.CanvasRenderer|PIXI.WebGLRenderer} renderer - The renderer used to generate the texture.
|
|
* @return {PIXI.RenderTexture} - A RenderTexture containing an image of this DisplayObject at the time it was invoked.
|
|
*/
|
|
generateTexture: function (resolution, scaleMode, renderer) {
|
|
|
|
var bounds = this.getLocalBounds();
|
|
|
|
var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution);
|
|
|
|
PIXI.DisplayObject._tempMatrix.tx = -bounds.x;
|
|
PIXI.DisplayObject._tempMatrix.ty = -bounds.y;
|
|
|
|
renderTexture.render(this, PIXI.DisplayObject._tempMatrix);
|
|
|
|
return renderTexture;
|
|
|
|
},
|
|
|
|
/**
|
|
* If this DisplayObject has a cached Sprite, this method generates and updates it.
|
|
*
|
|
* @method PIXI.DisplayObject#updateCache
|
|
* @return {PIXI.DisplayObject} - A reference to this DisplayObject.
|
|
*/
|
|
updateCache: function () {
|
|
|
|
this._generateCachedSprite();
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
/**
|
|
* Calculates the global position of this DisplayObject, based on the position given.
|
|
*
|
|
* @method PIXI.DisplayObject#toGlobal
|
|
* @param {PIXI.Point} position - The global position to calculate from.
|
|
* @return {PIXI.Point} - A point object representing the position of this DisplayObject based on the global position given.
|
|
*/
|
|
toGlobal: function (position) {
|
|
|
|
this.updateTransform();
|
|
|
|
return this.worldTransform.apply(position);
|
|
|
|
},
|
|
|
|
/**
|
|
* Calculates the local position of this DisplayObject, relative to another point.
|
|
*
|
|
* @method PIXI.DisplayObject#toLocal
|
|
* @param {PIXI.Point} position - The world origin to calculate from.
|
|
* @param {PIXI.DisplayObject} [from] - An optional DisplayObject to calculate the global position from.
|
|
* @return {PIXI.Point} - A point object representing the position of this DisplayObject based on the global position given.
|
|
*/
|
|
toLocal: function (position, from) {
|
|
|
|
if (from)
|
|
{
|
|
position = from.toGlobal(position);
|
|
}
|
|
|
|
this.updateTransform();
|
|
|
|
return this.worldTransform.applyInverse(position);
|
|
|
|
},
|
|
|
|
/**
|
|
* Internal method.
|
|
*
|
|
* @method PIXI.DisplayObject#_renderCachedSprite
|
|
* @private
|
|
* @param {Object} renderSession - The render session
|
|
*/
|
|
_renderCachedSprite: function (renderSession) {
|
|
|
|
this._cachedSprite.worldAlpha = this.worldAlpha;
|
|
|
|
if (renderSession.gl)
|
|
{
|
|
PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession);
|
|
}
|
|
else
|
|
{
|
|
PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession);
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Internal method.
|
|
*
|
|
* @method PIXI.DisplayObject#_generateCachedSprite
|
|
* @private
|
|
*/
|
|
_generateCachedSprite: function () {
|
|
|
|
this._cacheAsBitmap = false;
|
|
|
|
var bounds = this.getLocalBounds();
|
|
|
|
// Round it off and force non-zero dimensions
|
|
bounds.width = Math.max(1, Math.ceil(bounds.width));
|
|
bounds.height = Math.max(1, Math.ceil(bounds.height));
|
|
|
|
this.updateTransform();
|
|
|
|
if (!this._cachedSprite)
|
|
{
|
|
var textureUnit = 0;
|
|
if (this.texture && this.texture.baseTexture && PIXI._enableMultiTextureToggle)
|
|
textureUnit = this.texture.baseTexture.textureIndex;
|
|
var renderTexture = new PIXI.RenderTexture(bounds.width, bounds.height, null, null, null, textureUnit);
|
|
this._cachedSprite = new PIXI.Sprite(renderTexture);
|
|
this._cachedSprite.worldTransform = this.worldTransform;
|
|
}
|
|
else
|
|
{
|
|
this._cachedSprite.texture.resize(bounds.width, bounds.height);
|
|
}
|
|
|
|
// Remove filters
|
|
var tempFilters = this._filters;
|
|
|
|
this._filters = null;
|
|
this._cachedSprite.filters = tempFilters;
|
|
|
|
PIXI.DisplayObject._tempMatrix.tx = -bounds.x;
|
|
PIXI.DisplayObject._tempMatrix.ty = -bounds.y;
|
|
this._cachedSprite.texture.render(this, PIXI.DisplayObject._tempMatrix, true);
|
|
this._cachedSprite.anchor.x = -(bounds.x / bounds.width);
|
|
this._cachedSprite.anchor.y = -(bounds.y / bounds.height);
|
|
|
|
this._filters = tempFilters;
|
|
|
|
this._cacheAsBitmap = true;
|
|
|
|
},
|
|
|
|
/**
|
|
* Destroys a cached Sprite.
|
|
*
|
|
* @method PIXI.DisplayObject#_destroyCachedSprite
|
|
* @private
|
|
*/
|
|
_destroyCachedSprite: function () {
|
|
|
|
if (!this._cachedSprite)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this._cachedSprite.texture.destroy(true);
|
|
|
|
this._cachedSprite = null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Alias for updateTransform. As used in DisplayObject container, etc.
|
|
PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform;
|
|
|
|
Object.defineProperties(PIXI.DisplayObject.prototype, {
|
|
|
|
/**
|
|
* The horizontal position of the DisplayObject, in pixels, relative to its parent.
|
|
* If you need the world position of the DisplayObject, use `DisplayObject.worldPosition` instead.
|
|
* @name PIXI.DisplayObject#x
|
|
* @property {number} x - The horizontal position of the DisplayObject, in pixels, relative to its parent.
|
|
*/
|
|
'x': {
|
|
|
|
get: function () {
|
|
|
|
return this.position.x;
|
|
|
|
},
|
|
|
|
set: function (value) {
|
|
|
|
this.position.x = value;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* The vertical position of the DisplayObject, in pixels, relative to its parent.
|
|
* If you need the world position of the DisplayObject, use `DisplayObject.worldPosition` instead.
|
|
* @name PIXI.DisplayObject#y
|
|
* @property {number} y - The vertical position of the DisplayObject, in pixels, relative to its parent.
|
|
*/
|
|
'y': {
|
|
|
|
get: function () {
|
|
|
|
return this.position.y;
|
|
|
|
},
|
|
|
|
set: function (value) {
|
|
|
|
this.position.y = value;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Indicates if this DisplayObject is visible, based on it, and all of its parents, `visible` property values.
|
|
* @name PIXI.DisplayObject#worldVisible
|
|
* @property {boolean} worldVisible - Indicates if this DisplayObject is visible, based on it, and all of its parents, `visible` property values.
|
|
*/
|
|
'worldVisible': {
|
|
|
|
get: function () {
|
|
|
|
if (!this.visible)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
var item = this.parent;
|
|
|
|
if (!item)
|
|
{
|
|
return this.visible;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (!item.visible)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
item = item.parent;
|
|
}
|
|
while (item);
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Sets a mask for this DisplayObject. A mask is an instance of a Graphics object.
|
|
* When applied it limits the visible area of this DisplayObject to the shape of the mask.
|
|
* Under a Canvas renderer it uses shape clipping. Under a WebGL renderer it uses a Stencil Buffer.
|
|
* To remove a mask, set this property to `null`.
|
|
*
|
|
* @name PIXI.DisplayObject#mask
|
|
* @property {PIXI.Graphics} mask - The mask applied to this DisplayObject. Set to `null` to remove an existing mask.
|
|
*/
|
|
'mask': {
|
|
|
|
get: function () {
|
|
|
|
return this._mask;
|
|
|
|
},
|
|
|
|
set: function (value) {
|
|
|
|
if (this._mask)
|
|
{
|
|
this._mask.isMask = false;
|
|
}
|
|
|
|
this._mask = value;
|
|
|
|
if (value)
|
|
{
|
|
this._mask.isMask = true;
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Sets the filters for this DisplayObject. This is a WebGL only feature, and is ignored by the Canvas
|
|
* Renderer. A filter is a shader applied to this DisplayObject. You can modify the placement of the filter
|
|
* using `DisplayObject.filterArea`.
|
|
*
|
|
* To remove filters, set this property to `null`.
|
|
*
|
|
* Note: You cannot have a filter set, and a MULTIPLY Blend Mode active, at the same time. Setting a
|
|
* filter will reset this DisplayObjects blend mode to NORMAL.
|
|
*
|
|
* @name PIXI.DisplayObject#filters
|
|
* @property {Array} filters - An Array of PIXI.AbstractFilter objects, or objects that extend them.
|
|
*/
|
|
'filters': {
|
|
|
|
get: function () {
|
|
|
|
return this._filters;
|
|
|
|
},
|
|
|
|
set: function (value) {
|
|
|
|
if (Array.isArray(value))
|
|
{
|
|
// Put all the passes in one place.
|
|
var passes = [];
|
|
|
|
for (var i = 0; i < value.length; i++)
|
|
{
|
|
var filterPasses = value[i].passes;
|
|
|
|
for (var j = 0; j < filterPasses.length; j++)
|
|
{
|
|
passes.push(filterPasses[j]);
|
|
}
|
|
}
|
|
|
|
// Needed any more?
|
|
this._filterBlock = { target: this, filterPasses: passes };
|
|
}
|
|
|
|
this._filters = value;
|
|
|
|
if (this.blendMode && this.blendMode === PIXI.blendModes.MULTIPLY)
|
|
{
|
|
this.blendMode = PIXI.blendModes.NORMAL;
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Sets if this DisplayObject should be cached as a bitmap.
|
|
*
|
|
* When invoked it will take a snapshot of the DisplayObject, as it is at that moment, and store it
|
|
* in a RenderTexture. This is then used whenever this DisplayObject is rendered. It can provide a
|
|
* performance benefit for complex, but static, DisplayObjects. I.e. those with lots of children.
|
|
*
|
|
* Cached Bitmaps do not track their parents. If you update a property of this DisplayObject, it will not
|
|
* re-generate the cached bitmap automatically. To do that you need to call `DisplayObject.updateCache`.
|
|
*
|
|
* To remove a cached bitmap, set this property to `null`.
|
|
*
|
|
* @name PIXI.DisplayObject#cacheAsBitmap
|
|
* @property {boolean} cacheAsBitmap - Cache this DisplayObject as a Bitmap. Set to `null` to remove an existing cached bitmap.
|
|
*/
|
|
'cacheAsBitmap': {
|
|
|
|
get: function () {
|
|
|
|
return this._cacheAsBitmap;
|
|
|
|
},
|
|
|
|
set: function (value) {
|
|
|
|
if (this._cacheAsBitmap === value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (value)
|
|
{
|
|
this._generateCachedSprite();
|
|
}
|
|
else
|
|
{
|
|
this._destroyCachedSprite();
|
|
}
|
|
|
|
this._cacheAsBitmap = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
/**
|
|
* @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 array of children of this container.
|
|
*
|
|
* @property children
|
|
* @type Array(DisplayObject)
|
|
* @readOnly
|
|
*/
|
|
this.children = [];
|
|
|
|
/**
|
|
* If `ignoreChildInput` is `false` it will allow this objects _children_ to be considered as valid for Input events.
|
|
*
|
|
* If this property is `true` then the children will _not_ be considered as valid for Input events.
|
|
*
|
|
* Note that this property isn't recursive: only immediate children are influenced, it doesn't scan further down.
|
|
* @property {boolean} ignoreChildInput
|
|
* @default
|
|
*/
|
|
this.ignoreChildInput = false;
|
|
|
|
};
|
|
|
|
PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype );
|
|
PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer;
|
|
|
|
/**
|
|
* Adds a child to the container.
|
|
*
|
|
* @method addChild
|
|
* @param child {DisplayObject} The DisplayObject to add to the container
|
|
* @return {DisplayObject} The child that was added.
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.addChild = function (child) {
|
|
|
|
return this.addChildAt(child, this.children.length);
|
|
|
|
};
|
|
|
|
/**
|
|
* 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
|
|
* @return {DisplayObject} The child that was added.
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.addChildAt = function (child, index) {
|
|
|
|
if (index >= 0 && index <= this.children.length)
|
|
{
|
|
if (child.parent)
|
|
{
|
|
child.parent.removeChild(child);
|
|
}
|
|
|
|
child.parent = this;
|
|
|
|
this.children.splice(index, 0, child);
|
|
|
|
return child;
|
|
}
|
|
else
|
|
{
|
|
throw new Error(child + 'addChildAt: The index '+ index +' supplied is out of bounds ' + this.children.length);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Swaps the position of 2 Display Objects within this container.
|
|
*
|
|
* @method swapChildren
|
|
* @param child {DisplayObject}
|
|
* @param child2 {DisplayObject}
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.swapChildren = function (child, child2) {
|
|
|
|
if (child === child2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var index1 = this.getChildIndex(child);
|
|
var index2 = this.getChildIndex(child2);
|
|
|
|
if (index1 < 0 || index2 < 0)
|
|
{
|
|
throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.');
|
|
}
|
|
|
|
this.children[index1] = child2;
|
|
this.children[index2] = child;
|
|
|
|
};
|
|
|
|
/**
|
|
* Returns the index position of a child DisplayObject instance
|
|
*
|
|
* @method getChildIndex
|
|
* @param child {DisplayObject} The DisplayObject instance to identify
|
|
* @return {Number} The index position of the child display object to identify
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.getChildIndex = function (child) {
|
|
|
|
var index = this.children.indexOf(child);
|
|
|
|
if (index === -1)
|
|
{
|
|
throw new Error('The supplied DisplayObject must be a child of the caller');
|
|
}
|
|
|
|
return index;
|
|
|
|
};
|
|
|
|
/**
|
|
* Changes the position of an existing child in the display object container
|
|
*
|
|
* @method setChildIndex
|
|
* @param child {DisplayObject} The child DisplayObject instance for which you want to change the index number
|
|
* @param index {Number} The resulting index number for the child display object
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.setChildIndex = function (child, index) {
|
|
|
|
if (index < 0 || index >= this.children.length)
|
|
{
|
|
throw new Error('The supplied index is out of bounds');
|
|
}
|
|
|
|
var currentIndex = this.getChildIndex(child);
|
|
|
|
this.children.splice(currentIndex, 1); //remove from old position
|
|
this.children.splice(index, 0, child); //add at new position
|
|
|
|
};
|
|
|
|
/**
|
|
* Returns the child at the specified index
|
|
*
|
|
* @method getChildAt
|
|
* @param index {Number} The index to get the child from
|
|
* @return {DisplayObject} The child at the given index, if any.
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.getChildAt = function (index) {
|
|
|
|
if (index < 0 || index >= this.children.length)
|
|
{
|
|
throw new Error('getChildAt: Supplied index '+ index +' does not exist in the child list, or the supplied DisplayObject must be a child of the caller');
|
|
}
|
|
|
|
return this.children[index];
|
|
|
|
};
|
|
|
|
/**
|
|
* Removes a child from the container.
|
|
*
|
|
* @method removeChild
|
|
* @param child {DisplayObject} The DisplayObject to remove
|
|
* @return {DisplayObject} The child that was removed.
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.removeChild = function (child) {
|
|
|
|
var index = this.children.indexOf(child);
|
|
|
|
if (index === -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
return this.removeChildAt(index);
|
|
|
|
};
|
|
|
|
/**
|
|
* Removes a child from the specified index position.
|
|
*
|
|
* @method removeChildAt
|
|
* @param index {Number} The index to get the child from
|
|
* @return {DisplayObject} The child that was removed.
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.removeChildAt = function (index) {
|
|
|
|
var child = this.getChildAt(index);
|
|
|
|
if (child)
|
|
{
|
|
child.parent = undefined;
|
|
|
|
this.children.splice(index, 1);
|
|
}
|
|
|
|
return child;
|
|
|
|
};
|
|
|
|
/**
|
|
* Removes all children from this container that are within the begin and end indexes.
|
|
*
|
|
* @method removeChildren
|
|
* @param beginIndex {Number} The beginning position. Default value is 0.
|
|
* @param endIndex {Number} The ending position. Default value is size of the container.
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.removeChildren = function (beginIndex, endIndex) {
|
|
|
|
if (beginIndex === undefined) { beginIndex = 0; }
|
|
if (endIndex === undefined) { endIndex = this.children.length; }
|
|
|
|
var range = endIndex - beginIndex;
|
|
|
|
if (range > 0 && range <= endIndex)
|
|
{
|
|
var removed = this.children.splice(beginIndex, range);
|
|
|
|
for (var i = 0; i < removed.length; i++)
|
|
{
|
|
var child = removed[i];
|
|
child.parent = undefined;
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
else if (range === 0 && this.children.length === 0)
|
|
{
|
|
return [];
|
|
}
|
|
else
|
|
{
|
|
throw new Error( 'removeChildren: Range Error, numeric values are outside the acceptable range' );
|
|
}
|
|
|
|
};
|
|
|
|
/*
|
|
* Updates the transform on all children of this container for rendering
|
|
*
|
|
* @method updateTransform
|
|
* @private
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.updateTransform = function () {
|
|
|
|
if (!this.visible)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.displayObjectUpdateTransform();
|
|
|
|
if (this._cacheAsBitmap)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i].updateTransform();
|
|
}
|
|
|
|
};
|
|
|
|
// performance increase to avoid using call.. (10x faster)
|
|
PIXI.DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = PIXI.DisplayObjectContainer.prototype.updateTransform;
|
|
|
|
/**
|
|
* Retrieves the global bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration.
|
|
*
|
|
* @method getBounds
|
|
* @param {PIXI.DisplayObject|PIXI.Matrix} [targetCoordinateSpace] Returns a rectangle that defines the area of the display object relative to the coordinate system of the targetCoordinateSpace object.
|
|
* @return {Rectangle} The rectangular bounding area
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.getBounds = function (targetCoordinateSpace) {
|
|
|
|
var isTargetCoordinateSpaceDisplayObject = (targetCoordinateSpace && targetCoordinateSpace instanceof PIXI.DisplayObject);
|
|
var isTargetCoordinateSpaceThisOrParent = true;
|
|
|
|
if (!isTargetCoordinateSpaceDisplayObject)
|
|
{
|
|
targetCoordinateSpace = this;
|
|
}
|
|
else if (targetCoordinateSpace instanceof PIXI.DisplayObjectContainer)
|
|
{
|
|
isTargetCoordinateSpaceThisOrParent = targetCoordinateSpace.contains(this);
|
|
}
|
|
else
|
|
{
|
|
isTargetCoordinateSpaceThisOrParent = false;
|
|
}
|
|
|
|
var i;
|
|
|
|
if (isTargetCoordinateSpaceDisplayObject)
|
|
{
|
|
var matrixCache = targetCoordinateSpace.worldTransform;
|
|
|
|
targetCoordinateSpace.worldTransform = PIXI.identityMatrix;
|
|
|
|
for (i = 0; i < targetCoordinateSpace.children.length; i++)
|
|
{
|
|
targetCoordinateSpace.children[i].updateTransform();
|
|
}
|
|
}
|
|
|
|
var minX = Infinity;
|
|
var minY = Infinity;
|
|
|
|
var maxX = -Infinity;
|
|
var maxY = -Infinity;
|
|
|
|
var childBounds;
|
|
var childMaxX;
|
|
var childMaxY;
|
|
|
|
var childVisible = false;
|
|
|
|
for (i = 0; i < this.children.length; i++)
|
|
{
|
|
var child = this.children[i];
|
|
|
|
if (!child.visible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
childVisible = true;
|
|
|
|
childBounds = this.children[i].getBounds();
|
|
|
|
minX = (minX < childBounds.x) ? minX : childBounds.x;
|
|
minY = (minY < childBounds.y) ? minY : childBounds.y;
|
|
|
|
childMaxX = childBounds.width + childBounds.x;
|
|
childMaxY = childBounds.height + childBounds.y;
|
|
|
|
maxX = (maxX > childMaxX) ? maxX : childMaxX;
|
|
maxY = (maxY > childMaxY) ? maxY : childMaxY;
|
|
}
|
|
|
|
var bounds = this._bounds;
|
|
|
|
if (!childVisible)
|
|
{
|
|
bounds = new PIXI.Rectangle();
|
|
|
|
var w0 = bounds.x;
|
|
var w1 = bounds.width + bounds.x;
|
|
|
|
var h0 = bounds.y;
|
|
var h1 = bounds.height + bounds.y;
|
|
|
|
var worldTransform = this.worldTransform;
|
|
|
|
var a = worldTransform.a;
|
|
var b = worldTransform.b;
|
|
var c = worldTransform.c;
|
|
var d = worldTransform.d;
|
|
var tx = worldTransform.tx;
|
|
var ty = worldTransform.ty;
|
|
|
|
var x1 = a * w1 + c * h1 + tx;
|
|
var y1 = d * h1 + b * w1 + ty;
|
|
|
|
var x2 = a * w0 + c * h1 + tx;
|
|
var y2 = d * h1 + b * w0 + ty;
|
|
|
|
var x3 = a * w0 + c * h0 + tx;
|
|
var y3 = d * h0 + b * w0 + ty;
|
|
|
|
var x4 = a * w1 + c * h0 + tx;
|
|
var y4 = d * h0 + b * w1 + ty;
|
|
|
|
maxX = x1;
|
|
maxY = y1;
|
|
|
|
minX = x1;
|
|
minY = y1;
|
|
|
|
minX = x2 < minX ? x2 : minX;
|
|
minX = x3 < minX ? x3 : minX;
|
|
minX = x4 < minX ? x4 : minX;
|
|
|
|
minY = y2 < minY ? y2 : minY;
|
|
minY = y3 < minY ? y3 : minY;
|
|
minY = y4 < minY ? y4 : minY;
|
|
|
|
maxX = x2 > maxX ? x2 : maxX;
|
|
maxX = x3 > maxX ? x3 : maxX;
|
|
maxX = x4 > maxX ? x4 : maxX;
|
|
|
|
maxY = y2 > maxY ? y2 : maxY;
|
|
maxY = y3 > maxY ? y3 : maxY;
|
|
maxY = y4 > maxY ? y4 : maxY;
|
|
}
|
|
|
|
bounds.x = minX;
|
|
bounds.y = minY;
|
|
bounds.width = maxX - minX;
|
|
bounds.height = maxY - minY;
|
|
|
|
if (isTargetCoordinateSpaceDisplayObject)
|
|
{
|
|
targetCoordinateSpace.worldTransform = matrixCache;
|
|
|
|
for (i = 0; i < targetCoordinateSpace.children.length; i++)
|
|
{
|
|
targetCoordinateSpace.children[i].updateTransform();
|
|
}
|
|
}
|
|
|
|
if (!isTargetCoordinateSpaceThisOrParent)
|
|
{
|
|
var targetCoordinateSpaceBounds = targetCoordinateSpace.getBounds();
|
|
|
|
bounds.x -= targetCoordinateSpaceBounds.x;
|
|
bounds.y -= targetCoordinateSpaceBounds.y;
|
|
}
|
|
|
|
return bounds;
|
|
|
|
};
|
|
|
|
/**
|
|
* Retrieves the non-global local bounds of the displayObjectContainer as a rectangle without any transformations. The calculation takes all visible children into consideration.
|
|
*
|
|
* @method getLocalBounds
|
|
* @return {Rectangle} The rectangular bounding area
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.getLocalBounds = function () {
|
|
|
|
return this.getBounds(this);
|
|
|
|
};
|
|
|
|
/**
|
|
* Determines whether the specified display object is a child of the DisplayObjectContainer instance or the instance itself.
|
|
*
|
|
* @method contains
|
|
* @param {DisplayObject} child
|
|
* @returns {boolean}
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype.contains = function (child) {
|
|
|
|
if (!child)
|
|
{
|
|
return false;
|
|
}
|
|
else if (child === this)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return this.contains(child.parent);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the WebGL renderer
|
|
*
|
|
* @method _renderWebGL
|
|
* @param renderSession {RenderSession}
|
|
* @private
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype._renderWebGL = function (renderSession) {
|
|
|
|
if (!this.visible || this.alpha <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this._cacheAsBitmap)
|
|
{
|
|
this._renderCachedSprite(renderSession);
|
|
return;
|
|
}
|
|
|
|
var i;
|
|
|
|
if (this._mask || this._filters)
|
|
{
|
|
// push filter first as we need to ensure the stencil buffer is correct for any masking
|
|
if (this._filters)
|
|
{
|
|
renderSession.spriteBatch.flush();
|
|
renderSession.filterManager.pushFilter(this._filterBlock);
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.spriteBatch.stop();
|
|
renderSession.maskManager.pushMask(this.mask, renderSession);
|
|
renderSession.spriteBatch.start();
|
|
}
|
|
|
|
// simple render children!
|
|
for (i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderWebGL(renderSession);
|
|
}
|
|
|
|
renderSession.spriteBatch.stop();
|
|
|
|
if (this._mask) renderSession.maskManager.popMask(this._mask, renderSession);
|
|
if (this._filters) renderSession.filterManager.popFilter();
|
|
|
|
renderSession.spriteBatch.start();
|
|
}
|
|
else
|
|
{
|
|
// simple render children!
|
|
for (i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderWebGL(renderSession);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the Canvas renderer
|
|
*
|
|
* @method _renderCanvas
|
|
* @param renderSession {RenderSession}
|
|
* @private
|
|
*/
|
|
PIXI.DisplayObjectContainer.prototype._renderCanvas = function (renderSession) {
|
|
|
|
if (this.visible === false || this.alpha === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this._cacheAsBitmap)
|
|
{
|
|
this._renderCachedSprite(renderSession);
|
|
return;
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.maskManager.pushMask(this._mask, renderSession);
|
|
}
|
|
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderCanvas(renderSession);
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.maskManager.popMask(renderSession);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', {
|
|
|
|
get: function() {
|
|
return this.getLocalBounds().width * this.scale.x;
|
|
},
|
|
|
|
set: function(value) {
|
|
|
|
var width = this.getLocalBounds().width;
|
|
|
|
if (width !== 0)
|
|
{
|
|
this.scale.x = value / width;
|
|
}
|
|
else
|
|
{
|
|
this.scale.x = 1;
|
|
}
|
|
|
|
this._width = value;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', {
|
|
|
|
get: function() {
|
|
return this.getLocalBounds().height * this.scale.y;
|
|
},
|
|
|
|
set: function(value) {
|
|
|
|
var height = this.getLocalBounds().height;
|
|
|
|
if (height !== 0)
|
|
{
|
|
this.scale.y = value / height;
|
|
}
|
|
else
|
|
{
|
|
this.scale.y = 1;
|
|
}
|
|
|
|
this._height = value;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* The Sprite object is the base for all textured objects that are rendered to the screen
|
|
*
|
|
* @class Sprite
|
|
* @extends DisplayObjectContainer
|
|
* @constructor
|
|
* @param texture {Texture} The texture for this sprite
|
|
*/
|
|
PIXI.Sprite = function (texture) {
|
|
|
|
PIXI.DisplayObjectContainer.call(this);
|
|
|
|
/**
|
|
* The anchor sets the origin point of the texture.
|
|
* The default is 0,0 this means the texture's origin is the top left
|
|
* Setting than anchor to 0.5,0.5 means the textures origin is centered
|
|
* Setting the anchor to 1,1 would mean the textures origin points will be the bottom right corner
|
|
*
|
|
* @property anchor
|
|
* @type Point
|
|
*/
|
|
this.anchor = new PIXI.Point();
|
|
|
|
/**
|
|
* The texture that the sprite is using
|
|
*
|
|
* @property texture
|
|
* @type Texture
|
|
*/
|
|
this.texture = texture || PIXI.Texture.emptyTexture;
|
|
|
|
/**
|
|
* The width of the sprite (this is initially set by the texture)
|
|
*
|
|
* @property _width
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._width = 0;
|
|
|
|
/**
|
|
* The height of the sprite (this is initially set by the texture)
|
|
*
|
|
* @property _height
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._height = 0;
|
|
|
|
/**
|
|
* The tint applied to the sprite. This is a hex value. A value of 0xFFFFFF will remove any tint effect.
|
|
*
|
|
* @property tint
|
|
* @type Number
|
|
* @default 0xFFFFFF
|
|
*/
|
|
this.tint = 0xFFFFFF;
|
|
|
|
/**
|
|
* The tint applied to the sprite. This is a hex value. A value of 0xFFFFFF will remove any tint effect.
|
|
*
|
|
* @property cachedTint
|
|
* @private
|
|
* @type Number
|
|
* @default -1
|
|
*/
|
|
this.cachedTint = -1;
|
|
|
|
/**
|
|
* A canvas that contains the tinted version of the Sprite (in Canvas mode, WebGL doesn't populate this)
|
|
*
|
|
* @property tintedTexture
|
|
* @type Canvas
|
|
* @default null
|
|
*/
|
|
this.tintedTexture = null;
|
|
|
|
/**
|
|
* The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode.
|
|
*
|
|
* Warning: You cannot have a blend mode and a filter active on the same Sprite. Doing so will render the sprite invisible.
|
|
*
|
|
* @property blendMode
|
|
* @type Number
|
|
* @default PIXI.blendModes.NORMAL;
|
|
*/
|
|
this.blendMode = PIXI.blendModes.NORMAL;
|
|
|
|
/**
|
|
* The shader that will be used to render this Sprite.
|
|
* Set to null to remove a current shader.
|
|
*
|
|
* @property shader
|
|
* @type AbstractFilter
|
|
* @default null
|
|
*/
|
|
this.shader = null;
|
|
|
|
/**
|
|
* Controls if this Sprite is processed by the core Phaser game loops and Group loops.
|
|
*
|
|
* @property exists
|
|
* @type Boolean
|
|
* @default true
|
|
*/
|
|
this.exists = true;
|
|
|
|
if (this.texture.baseTexture.hasLoaded)
|
|
{
|
|
this.onTextureUpdate();
|
|
}
|
|
|
|
this.renderable = true;
|
|
|
|
};
|
|
|
|
// constructor
|
|
PIXI.Sprite.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
|
|
PIXI.Sprite.prototype.constructor = PIXI.Sprite;
|
|
|
|
/**
|
|
* The width of the sprite, setting this will actually modify the scale to achieve the value set
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
Object.defineProperty(PIXI.Sprite.prototype, 'width', {
|
|
|
|
get: function() {
|
|
return this.scale.x * this.texture.frame.width;
|
|
},
|
|
|
|
set: function(value) {
|
|
this.scale.x = value / this.texture.frame.width;
|
|
this._width = value;
|
|
}
|
|
|
|
});
|
|
|
|
/**
|
|
* The height of the sprite, setting this will actually modify the scale to achieve the value set
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
Object.defineProperty(PIXI.Sprite.prototype, 'height', {
|
|
|
|
get: function() {
|
|
return this.scale.y * this.texture.frame.height;
|
|
},
|
|
|
|
set: function(value) {
|
|
this.scale.y = value / this.texture.frame.height;
|
|
this._height = value;
|
|
}
|
|
|
|
});
|
|
|
|
/**
|
|
* Sets the texture of the sprite. Be warned that this doesn't remove or destroy the previous
|
|
* texture this Sprite was using.
|
|
*
|
|
* @method setTexture
|
|
* @param texture {Texture} The PIXI texture that is displayed by the sprite
|
|
* @param [destroy=false] {boolean} Call Texture.destroy on the current texture before replacing it with the new one?
|
|
*/
|
|
PIXI.Sprite.prototype.setTexture = function(texture, destroyBase)
|
|
{
|
|
if (destroyBase !== undefined)
|
|
{
|
|
this.texture.baseTexture.destroy();
|
|
}
|
|
|
|
// Over-ridden by loadTexture as needed
|
|
this.texture.baseTexture.skipRender = false;
|
|
this.texture = texture;
|
|
this.texture.valid = true;
|
|
this.cachedTint = -1;
|
|
};
|
|
|
|
/**
|
|
* When the texture is updated, this event will fire to update the scale and frame
|
|
*
|
|
* @method onTextureUpdate
|
|
* @param event
|
|
* @private
|
|
*/
|
|
PIXI.Sprite.prototype.onTextureUpdate = function()
|
|
{
|
|
// so if _width is 0 then width was not set..
|
|
if (this._width) this.scale.x = this._width / this.texture.frame.width;
|
|
if (this._height) this.scale.y = this._height / this.texture.frame.height;
|
|
};
|
|
|
|
/**
|
|
* Returns the bounds of the Sprite as a rectangle.
|
|
* The bounds calculation takes the worldTransform into account.
|
|
*
|
|
* It is important to note that the transform is not updated when you call this method.
|
|
* So if this Sprite is the child of a Display Object which has had its transform
|
|
* updated since the last render pass, those changes will not yet have been applied
|
|
* to this Sprites worldTransform. If you need to ensure that all parent transforms
|
|
* are factored into this getBounds operation then you should call `updateTransform`
|
|
* on the root most object in this Sprites display list first.
|
|
*
|
|
* @method getBounds
|
|
* @param matrix {Matrix} the transformation matrix of the sprite
|
|
* @return {Rectangle} the framing rectangle
|
|
*/
|
|
PIXI.Sprite.prototype.getBounds = function(matrix)
|
|
{
|
|
var width = this.texture.frame.width;
|
|
var height = this.texture.frame.height;
|
|
|
|
var w0 = width * (1-this.anchor.x);
|
|
var w1 = width * -this.anchor.x;
|
|
|
|
var h0 = height * (1-this.anchor.y);
|
|
var h1 = height * -this.anchor.y;
|
|
|
|
var worldTransform = matrix || this.worldTransform;
|
|
|
|
var a = worldTransform.a;
|
|
var b = worldTransform.b;
|
|
var c = worldTransform.c;
|
|
var d = worldTransform.d;
|
|
var tx = worldTransform.tx;
|
|
var ty = worldTransform.ty;
|
|
|
|
var maxX = -Infinity;
|
|
var maxY = -Infinity;
|
|
|
|
var minX = Infinity;
|
|
var minY = Infinity;
|
|
|
|
if (b === 0 && c === 0)
|
|
{
|
|
// scale may be negative!
|
|
if (a < 0)
|
|
{
|
|
a *= -1;
|
|
var temp = w0;
|
|
w0 = -w1;
|
|
w1 = -temp;
|
|
}
|
|
|
|
if (d < 0)
|
|
{
|
|
d *= -1;
|
|
var temp = h0;
|
|
h0 = -h1;
|
|
h1 = -temp;
|
|
}
|
|
|
|
// this means there is no rotation going on right? RIGHT?
|
|
// if thats the case then we can avoid checking the bound values! yay
|
|
minX = a * w1 + tx;
|
|
maxX = a * w0 + tx;
|
|
minY = d * h1 + ty;
|
|
maxY = d * h0 + ty;
|
|
}
|
|
else
|
|
{
|
|
var x1 = a * w1 + c * h1 + tx;
|
|
var y1 = d * h1 + b * w1 + ty;
|
|
|
|
var x2 = a * w0 + c * h1 + tx;
|
|
var y2 = d * h1 + b * w0 + ty;
|
|
|
|
var x3 = a * w0 + c * h0 + tx;
|
|
var y3 = d * h0 + b * w0 + ty;
|
|
|
|
var x4 = a * w1 + c * h0 + tx;
|
|
var y4 = d * h0 + b * w1 + ty;
|
|
|
|
minX = x1 < minX ? x1 : minX;
|
|
minX = x2 < minX ? x2 : minX;
|
|
minX = x3 < minX ? x3 : minX;
|
|
minX = x4 < minX ? x4 : minX;
|
|
|
|
minY = y1 < minY ? y1 : minY;
|
|
minY = y2 < minY ? y2 : minY;
|
|
minY = y3 < minY ? y3 : minY;
|
|
minY = y4 < minY ? y4 : minY;
|
|
|
|
maxX = x1 > maxX ? x1 : maxX;
|
|
maxX = x2 > maxX ? x2 : maxX;
|
|
maxX = x3 > maxX ? x3 : maxX;
|
|
maxX = x4 > maxX ? x4 : maxX;
|
|
|
|
maxY = y1 > maxY ? y1 : maxY;
|
|
maxY = y2 > maxY ? y2 : maxY;
|
|
maxY = y3 > maxY ? y3 : maxY;
|
|
maxY = y4 > maxY ? y4 : maxY;
|
|
}
|
|
|
|
var bounds = this._bounds;
|
|
|
|
bounds.x = minX;
|
|
bounds.width = maxX - minX;
|
|
|
|
bounds.y = minY;
|
|
bounds.height = maxY - minY;
|
|
|
|
// store a reference so that if this function gets called again in the render cycle we do not have to recalculate
|
|
this._currentBounds = bounds;
|
|
|
|
return bounds;
|
|
};
|
|
|
|
/**
|
|
* Retrieves the non-global local bounds of the Sprite as a rectangle. The calculation takes all visible children into consideration.
|
|
*
|
|
* @method getLocalBounds
|
|
* @return {Rectangle} The rectangular bounding area
|
|
*/
|
|
PIXI.Sprite.prototype.getLocalBounds = function () {
|
|
|
|
var matrixCache = this.worldTransform;
|
|
|
|
this.worldTransform = PIXI.identityMatrix;
|
|
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i].updateTransform();
|
|
}
|
|
|
|
var bounds = this.getBounds();
|
|
|
|
this.worldTransform = matrixCache;
|
|
|
|
for (i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i].updateTransform();
|
|
}
|
|
|
|
return bounds;
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the WebGL renderer
|
|
*
|
|
* @method _renderWebGL
|
|
* @param renderSession {RenderSession}
|
|
* @param {Matrix} [matrix] - Optional matrix. If provided the Display Object will be rendered using this matrix, otherwise it will use its worldTransform.
|
|
* @private
|
|
*/
|
|
PIXI.Sprite.prototype._renderWebGL = function(renderSession, matrix)
|
|
{
|
|
// if the sprite is not visible or the alpha is 0 then no need to render this element
|
|
if (!this.visible || this.alpha <= 0 || !this.renderable) return;
|
|
|
|
// They provided an alternative rendering matrix, so use it
|
|
var wt = this.worldTransform;
|
|
|
|
if (matrix)
|
|
{
|
|
wt = matrix;
|
|
}
|
|
|
|
// A quick check to see if this element has a mask or a filter.
|
|
if (this._mask || this._filters)
|
|
{
|
|
var spriteBatch = renderSession.spriteBatch;
|
|
|
|
// push filter first as we need to ensure the stencil buffer is correct for any masking
|
|
if (this._filters)
|
|
{
|
|
spriteBatch.flush();
|
|
renderSession.filterManager.pushFilter(this._filterBlock);
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
spriteBatch.stop();
|
|
renderSession.maskManager.pushMask(this.mask, renderSession);
|
|
spriteBatch.start();
|
|
}
|
|
|
|
// add this sprite to the batch
|
|
spriteBatch.render(this);
|
|
|
|
// now loop through the children and make sure they get rendered
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderWebGL(renderSession);
|
|
}
|
|
|
|
// time to stop the sprite batch as either a mask element or a filter draw will happen next
|
|
spriteBatch.stop();
|
|
|
|
if (this._mask) renderSession.maskManager.popMask(this._mask, renderSession);
|
|
if (this._filters) renderSession.filterManager.popFilter();
|
|
|
|
spriteBatch.start();
|
|
}
|
|
else
|
|
{
|
|
renderSession.spriteBatch.render(this);
|
|
|
|
// Render children!
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderWebGL(renderSession, wt);
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the Canvas renderer
|
|
*
|
|
* @method _renderCanvas
|
|
* @param renderSession {RenderSession}
|
|
* @param {Matrix} [matrix] - Optional matrix. If provided the Display Object will be rendered using this matrix, otherwise it will use its worldTransform.
|
|
* @private
|
|
*/
|
|
PIXI.Sprite.prototype._renderCanvas = function(renderSession, matrix)
|
|
{
|
|
// If the sprite is not visible or the alpha is 0 then no need to render this element
|
|
if (!this.visible || this.alpha === 0 || !this.renderable || this.texture.crop.width <= 0 || this.texture.crop.height <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var wt = this.worldTransform;
|
|
|
|
// If they provided an alternative rendering matrix then use it
|
|
if (matrix)
|
|
{
|
|
wt = matrix;
|
|
}
|
|
|
|
if (this.blendMode !== renderSession.currentBlendMode)
|
|
{
|
|
renderSession.currentBlendMode = this.blendMode;
|
|
renderSession.context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode];
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.maskManager.pushMask(this._mask, renderSession);
|
|
}
|
|
|
|
// Ignore null sources
|
|
if (!this.texture.valid)
|
|
{
|
|
// Update the children and leave
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderCanvas(renderSession);
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.maskManager.popMask(renderSession);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
var resolution = this.texture.baseTexture.resolution / renderSession.resolution;
|
|
|
|
renderSession.context.globalAlpha = this.worldAlpha;
|
|
|
|
// If smoothingEnabled is supported and we need to change the smoothing property for this texture
|
|
if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode)
|
|
{
|
|
renderSession.scaleMode = this.texture.baseTexture.scaleMode;
|
|
renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === PIXI.scaleModes.LINEAR);
|
|
}
|
|
|
|
// If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions
|
|
var dx = (this.texture.trim) ? this.texture.trim.x - this.anchor.x * this.texture.trim.width : this.anchor.x * -this.texture.frame.width;
|
|
var dy = (this.texture.trim) ? this.texture.trim.y - this.anchor.y * this.texture.trim.height : this.anchor.y * -this.texture.frame.height;
|
|
|
|
var tx = (wt.tx * renderSession.resolution) + renderSession.shakeX;
|
|
var ty = (wt.ty * renderSession.resolution) + renderSession.shakeY;
|
|
|
|
var cw = this.texture.crop.width;
|
|
var ch = this.texture.crop.height;
|
|
|
|
if (this.texture.rotated)
|
|
{
|
|
var a = wt.a;
|
|
var b = wt.b;
|
|
var c = wt.c;
|
|
var d = wt.d;
|
|
var e = cw;
|
|
|
|
// Offset before rotating
|
|
tx = wt.c * ch + tx;
|
|
ty = wt.d * ch + ty;
|
|
|
|
// Rotate matrix by 90 degrees
|
|
// We use precalculated values for sine and cosine of rad(90)
|
|
wt.a = a * 6.123233995736766e-17 + -c;
|
|
wt.b = b * 6.123233995736766e-17 + -d;
|
|
wt.c = a + c * 6.123233995736766e-17;
|
|
wt.d = b + d * 6.123233995736766e-17;
|
|
|
|
// Update cropping dimensions.
|
|
cw = ch;
|
|
ch = e;
|
|
}
|
|
|
|
// Allow for pixel rounding
|
|
if (renderSession.roundPixels)
|
|
{
|
|
renderSession.context.setTransform(wt.a, wt.b, wt.c, wt.d, tx | 0, ty | 0);
|
|
dx |= 0;
|
|
dy |= 0;
|
|
}
|
|
else
|
|
{
|
|
renderSession.context.setTransform(wt.a, wt.b, wt.c, wt.d, tx, ty);
|
|
}
|
|
|
|
dx /= resolution;
|
|
dy /= resolution;
|
|
|
|
if (this.tint !== 0xFFFFFF)
|
|
{
|
|
if (this.texture.requiresReTint || this.cachedTint !== this.tint)
|
|
{
|
|
this.tintedTexture = PIXI.CanvasTinter.getTintedTexture(this, this.tint);
|
|
|
|
this.cachedTint = this.tint;
|
|
this.texture.requiresReTint = false;
|
|
}
|
|
|
|
renderSession.context.drawImage(this.tintedTexture, 0, 0, cw, ch, dx, dy, cw / resolution, ch / resolution);
|
|
}
|
|
else
|
|
{
|
|
var cx = this.texture.crop.x;
|
|
var cy = this.texture.crop.y;
|
|
|
|
renderSession.context.drawImage(this.texture.baseTexture.source, cx, cy, cw, ch, dx, dy, cw / resolution, ch / resolution);
|
|
}
|
|
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderCanvas(renderSession);
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.maskManager.popMask(renderSession);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/
|
|
*/
|
|
|
|
/**
|
|
* The SpriteBatch class is a really fast version of the DisplayObjectContainer
|
|
* built solely for speed, so use when you need a lot of sprites or particles.
|
|
* And it's extremely easy to use :
|
|
|
|
var container = new PIXI.SpriteBatch();
|
|
|
|
for(var i = 0; i < 100; i++)
|
|
{
|
|
var sprite = new PIXI.Sprite.fromImage("myImage.png");
|
|
container.addChild(sprite);
|
|
}
|
|
* And here you have a hundred sprites that will be renderer at the speed of light
|
|
*
|
|
* @class SpriteBatch
|
|
* @constructor
|
|
* @param texture {Texture}
|
|
*/
|
|
PIXI.SpriteBatch = function(texture)
|
|
{
|
|
PIXI.DisplayObjectContainer.call( this);
|
|
|
|
this.textureThing = texture;
|
|
|
|
this.ready = false;
|
|
};
|
|
|
|
PIXI.SpriteBatch.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
|
|
PIXI.SpriteBatch.prototype.constructor = PIXI.SpriteBatch;
|
|
|
|
/*
|
|
* Initialises the spriteBatch
|
|
*
|
|
* @method initWebGL
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.SpriteBatch.prototype.initWebGL = function(gl)
|
|
{
|
|
// TODO only one needed for the whole engine really?
|
|
this.fastSpriteBatch = new PIXI.WebGLFastSpriteBatch(gl);
|
|
|
|
this.ready = true;
|
|
};
|
|
|
|
/*
|
|
* Updates the object transform for rendering
|
|
*
|
|
* @method updateTransform
|
|
* @private
|
|
*/
|
|
PIXI.SpriteBatch.prototype.updateTransform = function()
|
|
{
|
|
// TODO don't need to!
|
|
this.displayObjectUpdateTransform();
|
|
// PIXI.DisplayObjectContainer.prototype.updateTransform.call( this );
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the WebGL renderer
|
|
*
|
|
* @method _renderWebGL
|
|
* @param renderSession {RenderSession}
|
|
* @private
|
|
*/
|
|
PIXI.SpriteBatch.prototype._renderWebGL = function(renderSession)
|
|
{
|
|
if (!this.visible || this.alpha <= 0 || !this.children.length) return;
|
|
|
|
if (!this.ready)
|
|
{
|
|
this.initWebGL(renderSession.gl);
|
|
}
|
|
|
|
if (this.fastSpriteBatch.gl !== renderSession.gl)
|
|
{
|
|
this.fastSpriteBatch.setContext(renderSession.gl);
|
|
}
|
|
|
|
renderSession.spriteBatch.stop();
|
|
|
|
renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader);
|
|
|
|
this.fastSpriteBatch.begin(this, renderSession);
|
|
this.fastSpriteBatch.render(this);
|
|
|
|
renderSession.spriteBatch.start();
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the Canvas renderer
|
|
*
|
|
* @method _renderCanvas
|
|
* @param renderSession {RenderSession}
|
|
* @private
|
|
*/
|
|
PIXI.SpriteBatch.prototype._renderCanvas = function(renderSession)
|
|
{
|
|
if (!this.visible || this.alpha <= 0 || !this.children.length) return;
|
|
|
|
var context = renderSession.context;
|
|
|
|
context.globalAlpha = this.worldAlpha;
|
|
|
|
this.displayObjectUpdateTransform();
|
|
|
|
var transform = this.worldTransform;
|
|
|
|
var isRotated = true;
|
|
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
var child = this.children[i];
|
|
|
|
if (!child.visible) continue;
|
|
|
|
var texture = child.texture;
|
|
var frame = texture.frame;
|
|
|
|
context.globalAlpha = this.worldAlpha * child.alpha;
|
|
|
|
if (child.rotation % (Math.PI * 2) === 0)
|
|
{
|
|
if (isRotated)
|
|
{
|
|
context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty);
|
|
isRotated = false;
|
|
}
|
|
|
|
// this is the fastest way to optimise! - if rotation is 0 then we can avoid any kind of setTransform call
|
|
context.drawImage(texture.baseTexture.source,
|
|
frame.x,
|
|
frame.y,
|
|
frame.width,
|
|
frame.height,
|
|
((child.anchor.x) * (-frame.width * child.scale.x) + child.position.x + 0.5 + renderSession.shakeX) | 0,
|
|
((child.anchor.y) * (-frame.height * child.scale.y) + child.position.y + 0.5 + renderSession.shakeY) | 0,
|
|
frame.width * child.scale.x,
|
|
frame.height * child.scale.y);
|
|
}
|
|
else
|
|
{
|
|
if (!isRotated) isRotated = true;
|
|
|
|
child.displayObjectUpdateTransform();
|
|
|
|
var childTransform = child.worldTransform;
|
|
var tx = (childTransform.tx * renderSession.resolution) + renderSession.shakeX;
|
|
var ty = (childTransform.ty * renderSession.resolution) + renderSession.shakeY;
|
|
|
|
// allow for trimming
|
|
|
|
if (renderSession.roundPixels)
|
|
{
|
|
context.setTransform(childTransform.a, childTransform.b, childTransform.c, childTransform.d, tx | 0, ty | 0);
|
|
}
|
|
else
|
|
{
|
|
context.setTransform(childTransform.a, childTransform.b, childTransform.c, childTransform.d, tx, ty);
|
|
}
|
|
|
|
context.drawImage(texture.baseTexture.source,
|
|
frame.x,
|
|
frame.y,
|
|
frame.width,
|
|
frame.height,
|
|
((child.anchor.x) * (-frame.width) + 0.5) | 0,
|
|
((child.anchor.y) * (-frame.height) + 0.5) | 0,
|
|
frame.width,
|
|
frame.height);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* Converts a hex color number to an [R, G, B] array
|
|
*
|
|
* @method hex2rgb
|
|
* @param hex {Number}
|
|
*/
|
|
PIXI.hex2rgb = function(hex) {
|
|
return [(hex >> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255];
|
|
};
|
|
|
|
/**
|
|
* Converts a color as an [R, G, B] array to a hex number
|
|
*
|
|
* @method rgb2hex
|
|
* @param rgb {Array}
|
|
*/
|
|
PIXI.rgb2hex = function(rgb) {
|
|
return ((rgb[0]*255 << 16) + (rgb[1]*255 << 8) + rgb[2]*255);
|
|
};
|
|
|
|
/**
|
|
* Checks whether the Canvas BlendModes are supported by the current browser for drawImage
|
|
*
|
|
* @method canUseNewCanvasBlendModes
|
|
* @return {Boolean} whether they are supported
|
|
*/
|
|
PIXI.canUseNewCanvasBlendModes = function()
|
|
{
|
|
if (document === undefined) return false;
|
|
|
|
var pngHead = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABAQMAAADD8p2OAAAAA1BMVEX/';
|
|
var pngEnd = 'AAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==';
|
|
|
|
var magenta = new Image();
|
|
magenta.src = pngHead + 'AP804Oa6' + pngEnd;
|
|
|
|
var yellow = new Image();
|
|
yellow.src = pngHead + '/wCKxvRF' + pngEnd;
|
|
|
|
var canvas = PIXI.CanvasPool.create(this, 6, 1);
|
|
var context = canvas.getContext('2d');
|
|
context.globalCompositeOperation = 'multiply';
|
|
context.drawImage(magenta, 0, 0);
|
|
context.drawImage(yellow, 2, 0);
|
|
|
|
if (!context.getImageData(2,0,1,1))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var data = context.getImageData(2,0,1,1).data;
|
|
|
|
PIXI.CanvasPool.remove(this);
|
|
|
|
return (data[0] === 255 && data[1] === 0 && data[2] === 0);
|
|
|
|
};
|
|
|
|
/**
|
|
* Given a number, this function returns the closest number that is a power of two
|
|
* this function is taken from Starling Framework as its pretty neat ;)
|
|
*
|
|
* @method getNextPowerOfTwo
|
|
* @param number {Number}
|
|
* @return {Number} the closest number that is a power of two
|
|
*/
|
|
PIXI.getNextPowerOfTwo = function(number)
|
|
{
|
|
if (number > 0 && (number & (number - 1)) === 0) // see: http://goo.gl/D9kPj
|
|
return number;
|
|
else
|
|
{
|
|
var result = 1;
|
|
while (result < number) result <<= 1;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* checks if the given width and height make a power of two texture
|
|
* @method isPowerOfTwo
|
|
* @param width {Number}
|
|
* @param height {Number}
|
|
* @return {Boolean}
|
|
*/
|
|
PIXI.isPowerOfTwo = function(width, height)
|
|
{
|
|
return (width > 0 && (width & (width - 1)) === 0 && height > 0 && (height & (height - 1)) === 0);
|
|
|
|
};
|
|
|
|
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @copyright 2016 Photon Storm Ltd.
|
|
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
|
*/
|
|
|
|
/**
|
|
* The CanvasPool is a global static object that allows Pixi and Phaser to pool canvas DOM elements.
|
|
*
|
|
* @class CanvasPool
|
|
* @static
|
|
*/
|
|
PIXI.CanvasPool = {
|
|
|
|
/**
|
|
* Creates a new Canvas DOM element, or pulls one from the pool if free.
|
|
*
|
|
* @method create
|
|
* @static
|
|
* @param parent {any} The parent of the canvas element.
|
|
* @param width {number} The width of the canvas element.
|
|
* @param height {number} The height of the canvas element.
|
|
* @return {HTMLCanvasElement} The canvas element.
|
|
*/
|
|
create: function (parent, width, height) {
|
|
|
|
var idx = PIXI.CanvasPool.getFirst();
|
|
var canvas;
|
|
|
|
if (idx === -1)
|
|
{
|
|
var container = {
|
|
parent: parent,
|
|
canvas: document.createElement('canvas')
|
|
}
|
|
|
|
PIXI.CanvasPool.pool.push(container);
|
|
|
|
canvas = container.canvas;
|
|
}
|
|
else
|
|
{
|
|
PIXI.CanvasPool.pool[idx].parent = parent;
|
|
|
|
canvas = PIXI.CanvasPool.pool[idx].canvas;
|
|
}
|
|
|
|
if (width !== undefined)
|
|
{
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
}
|
|
|
|
return canvas;
|
|
|
|
},
|
|
|
|
/**
|
|
* Gets the first free canvas index from the pool.
|
|
*
|
|
* @method getFirst
|
|
* @static
|
|
* @return {number}
|
|
*/
|
|
getFirst: function () {
|
|
|
|
var pool = PIXI.CanvasPool.pool;
|
|
|
|
for (var i = 0; i < pool.length; i++)
|
|
{
|
|
if (!pool[i].parent)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
|
|
},
|
|
|
|
/**
|
|
* Removes the parent from a canvas element from the pool, freeing it up for re-use.
|
|
*
|
|
* @method remove
|
|
* @param parent {any} The parent of the canvas element.
|
|
* @static
|
|
*/
|
|
remove: function (parent) {
|
|
|
|
var pool = PIXI.CanvasPool.pool;
|
|
|
|
for (var i = 0; i < pool.length; i++)
|
|
{
|
|
if (pool[i].parent === parent)
|
|
{
|
|
pool[i].parent = null;
|
|
pool[i].canvas.width = 1;
|
|
pool[i].canvas.height = 1;
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Removes the parent from a canvas element from the pool, freeing it up for re-use.
|
|
*
|
|
* @method removeByCanvas
|
|
* @param canvas {HTMLCanvasElement} The canvas element to remove
|
|
* @static
|
|
*/
|
|
removeByCanvas: function (canvas) {
|
|
|
|
var pool = PIXI.CanvasPool.pool;
|
|
|
|
for (var i = 0; i < pool.length; i++)
|
|
{
|
|
if (pool[i].canvas === canvas)
|
|
{
|
|
pool[i].parent = null;
|
|
pool[i].canvas.width = 1;
|
|
pool[i].canvas.height = 1;
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Gets the total number of used canvas elements in the pool.
|
|
*
|
|
* @method getTotal
|
|
* @static
|
|
* @return {number} The number of in-use (parented) canvas elements in the pool.
|
|
*/
|
|
getTotal: function () {
|
|
|
|
var pool = PIXI.CanvasPool.pool;
|
|
var c = 0;
|
|
|
|
for (var i = 0; i < pool.length; i++)
|
|
{
|
|
if (pool[i].parent)
|
|
{
|
|
c++;
|
|
}
|
|
}
|
|
|
|
return c;
|
|
|
|
},
|
|
|
|
/**
|
|
* Gets the total number of free canvas elements in the pool.
|
|
*
|
|
* @method getFree
|
|
* @static
|
|
* @return {number} The number of free (un-parented) canvas elements in the pool.
|
|
*/
|
|
getFree: function () {
|
|
|
|
var pool = PIXI.CanvasPool.pool;
|
|
var c = 0;
|
|
|
|
for (var i = 0; i < pool.length; i++)
|
|
{
|
|
if (!pool[i].parent)
|
|
{
|
|
c++;
|
|
}
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* The pool into which the canvas dom elements are placed.
|
|
*
|
|
* @property pool
|
|
* @type Array
|
|
* @static
|
|
*/
|
|
PIXI.CanvasPool.pool = [];
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @method initDefaultShaders
|
|
* @static
|
|
* @private
|
|
*/
|
|
PIXI.initDefaultShaders = function()
|
|
{
|
|
};
|
|
|
|
/**
|
|
* @method CompileVertexShader
|
|
* @static
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
* @param shaderSrc {Array}
|
|
* @return {Any}
|
|
*/
|
|
PIXI.CompileVertexShader = function(gl, shaderSrc)
|
|
{
|
|
return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER);
|
|
};
|
|
|
|
/**
|
|
* @method CompileFragmentShader
|
|
* @static
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
* @param shaderSrc {Array}
|
|
* @return {Any}
|
|
*/
|
|
PIXI.CompileFragmentShader = function(gl, shaderSrc)
|
|
{
|
|
return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER);
|
|
};
|
|
|
|
/**
|
|
* @method _CompileShader
|
|
* @static
|
|
* @private
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
* @param shaderSrc {Array}
|
|
* @param shaderType {Number}
|
|
* @return {Any}
|
|
*/
|
|
PIXI._CompileShader = function(gl, shaderSrc, shaderType)
|
|
{
|
|
var src = shaderSrc;
|
|
|
|
if (Array.isArray(shaderSrc))
|
|
{
|
|
src = shaderSrc.join("\n");
|
|
}
|
|
|
|
var shader = gl.createShader(shaderType);
|
|
gl.shaderSource(shader, src);
|
|
gl.compileShader(shader);
|
|
|
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
|
|
{
|
|
window.console.log(gl.getShaderInfoLog(shader));
|
|
return null;
|
|
}
|
|
|
|
return shader;
|
|
};
|
|
|
|
/**
|
|
* @method compileProgram
|
|
* @static
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
* @param vertexSrc {Array}
|
|
* @param fragmentSrc {Array}
|
|
* @return {Any}
|
|
*/
|
|
PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc)
|
|
{
|
|
var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc);
|
|
var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc);
|
|
|
|
var shaderProgram = gl.createProgram();
|
|
|
|
gl.attachShader(shaderProgram, vertexShader);
|
|
gl.attachShader(shaderProgram, fragmentShader);
|
|
gl.linkProgram(shaderProgram);
|
|
|
|
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
|
|
{
|
|
window.console.log(gl.getProgramInfoLog(shaderProgram));
|
|
window.console.log("Could not initialise shaders");
|
|
}
|
|
|
|
return shaderProgram;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
* @author Richard Davey http://www.photonstorm.com @photonstorm
|
|
*/
|
|
|
|
/**
|
|
* @class PixiShader
|
|
* @constructor
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.PixiShader = function(gl)
|
|
{
|
|
/**
|
|
* @property _UID
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._UID = PIXI._UID++;
|
|
|
|
/**
|
|
* @property gl
|
|
* @type WebGLContext
|
|
*/
|
|
this.gl = gl;
|
|
|
|
/**
|
|
* The WebGL program.
|
|
* @property program
|
|
* @type Any
|
|
*/
|
|
this.program = null;
|
|
|
|
/**
|
|
* The fragment shader.
|
|
* @property fragmentSrc
|
|
* @type Array
|
|
*/
|
|
this.fragmentSrc = null;
|
|
|
|
/**
|
|
* A local texture counter for multi-texture shaders.
|
|
* @property textureCount
|
|
* @type Number
|
|
*/
|
|
this.textureCount = 0;
|
|
|
|
/**
|
|
* A local flag
|
|
* @property firstRun
|
|
* @type Boolean
|
|
* @private
|
|
*/
|
|
this.firstRun = true;
|
|
|
|
/**
|
|
* A dirty flag
|
|
* @property dirty
|
|
* @type Boolean
|
|
*/
|
|
this.dirty = true;
|
|
|
|
/**
|
|
* Uniform attributes cache.
|
|
* @property attributes
|
|
* @type Array
|
|
* @private
|
|
*/
|
|
this.attributes = [];
|
|
|
|
this.init();
|
|
};
|
|
|
|
PIXI.PixiShader.prototype.constructor = PIXI.PixiShader;
|
|
|
|
PIXI.PixiShader.prototype.initMultitexShader = function () {
|
|
var gl = this.gl;
|
|
this.MAX_TEXTURES = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;\n'
|
|
for (var index = 1; index < this.MAX_TEXTURES; ++index)
|
|
{
|
|
dynamicIfs += '\telse if (vTextureIndex == ' +
|
|
index + '.0) gl_FragColor = texture2D(uSamplerArray[' +
|
|
index + '], vTextureCoord) * vColor;\n'
|
|
}
|
|
this.fragmentSrc = [
|
|
'// PixiShader Fragment Shader.',
|
|
'precision lowp float;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying vec4 vColor;',
|
|
'varying float vTextureIndex;',
|
|
'uniform sampler2D uSamplerArray[' + this.MAX_TEXTURES + '];',
|
|
'const vec4 PINK = vec4(1.0, 0.0, 1.0, 1.0);',
|
|
'const vec4 GREEN = vec4(0.0, 1.0, 0.0, 1.0);',
|
|
'void main(void) {',
|
|
dynamicIfs,
|
|
'else gl_FragColor = PINK;',
|
|
'}'
|
|
];
|
|
|
|
var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc);
|
|
|
|
gl.useProgram(program);
|
|
|
|
// get and store the uniforms for the shader
|
|
//this.uSampler = gl.getUniformLocation(program, 'uSampler');
|
|
this.uSamplerArray = gl.getUniformLocation(program, 'uSamplerArray[0]');
|
|
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
|
|
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
|
|
this.dimensions = gl.getUniformLocation(program, 'dimensions');
|
|
|
|
// get and store the attributes
|
|
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
|
|
this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
|
|
this.colorAttribute = gl.getAttribLocation(program, 'aColor');
|
|
this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex');
|
|
|
|
var indices = [];
|
|
// HACK: we bind an empty texture to avoid WebGL warning spam.
|
|
var tempTexture = gl.createTexture();
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, tempTexture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
|
|
for (var i = 0; i < this.MAX_TEXTURES; ++i) {
|
|
gl.activeTexture(gl.TEXTURE0 + i);
|
|
gl.bindTexture(gl.TEXTURE_2D, tempTexture);
|
|
indices.push(i);
|
|
}
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.uniform1iv(this.uSamplerArray, indices);
|
|
|
|
// Begin worst hack eva //
|
|
|
|
// WHY??? ONLY on my chrome pixel the line above returns -1 when using filters?
|
|
// maybe its something to do with the current state of the gl context.
|
|
// I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel
|
|
// If theres any webGL people that know why could happen please help :)
|
|
if(this.colorAttribute === -1)
|
|
{
|
|
this.colorAttribute = 2;
|
|
}
|
|
|
|
this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute, this.aTextureIndex];
|
|
|
|
// End worst hack eva //
|
|
|
|
// add those custom shaders!
|
|
for (var key in this.uniforms)
|
|
{
|
|
// get the uniform locations..
|
|
this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key);
|
|
}
|
|
|
|
this.initUniforms();
|
|
|
|
this.program = program;
|
|
};
|
|
|
|
PIXI.PixiShader.prototype.initDefaultShader = function () {
|
|
|
|
if (this.fragmentSrc === null) {
|
|
this.fragmentSrc = [
|
|
'precision lowp float;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying vec4 vColor;',
|
|
'varying float vTextureIndex;',
|
|
'uniform sampler2D uSampler;',
|
|
'void main(void) {',
|
|
' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;',
|
|
'}'
|
|
];
|
|
}
|
|
|
|
var gl = this.gl;
|
|
|
|
var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc);
|
|
|
|
gl.useProgram(program);
|
|
|
|
// get and store the uniforms for the shader
|
|
this.uSampler = gl.getUniformLocation(program, 'uSampler');
|
|
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
|
|
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
|
|
this.dimensions = gl.getUniformLocation(program, 'dimensions');
|
|
|
|
// get and store the attributes
|
|
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
|
|
this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
|
|
this.colorAttribute = gl.getAttribLocation(program, 'aColor');
|
|
this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex');
|
|
|
|
|
|
// Begin worst hack eva //
|
|
|
|
// WHY??? ONLY on my chrome pixel the line above returns -1 when using filters?
|
|
// maybe its something to do with the current state of the gl context.
|
|
// I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel
|
|
// If theres any webGL people that know why could happen please help :)
|
|
if(this.colorAttribute === -1)
|
|
{
|
|
this.colorAttribute = 2;
|
|
}
|
|
|
|
this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute, this.aTextureIndex];
|
|
|
|
// End worst hack eva //
|
|
|
|
// add those custom shaders!
|
|
for (var key in this.uniforms)
|
|
{
|
|
// get the uniform locations..
|
|
this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key);
|
|
}
|
|
|
|
this.initUniforms();
|
|
|
|
this.program = program;
|
|
};
|
|
/**
|
|
* Initialises the shader.
|
|
*
|
|
* @method init
|
|
*/
|
|
PIXI.PixiShader.prototype.init = function(usingFilter)
|
|
{
|
|
if (PIXI._enableMultiTextureToggle && !usingFilter) {
|
|
this.initMultitexShader();
|
|
} else {
|
|
this.initDefaultShader();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialises the shader uniform values.
|
|
*
|
|
* Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/
|
|
* http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf
|
|
*
|
|
* @method initUniforms
|
|
*/
|
|
PIXI.PixiShader.prototype.initUniforms = function()
|
|
{
|
|
this.textureCount = 1;
|
|
var gl = this.gl;
|
|
var uniform;
|
|
|
|
for (var key in this.uniforms)
|
|
{
|
|
uniform = this.uniforms[key];
|
|
|
|
var type = uniform.type;
|
|
|
|
if (type === 'sampler2D')
|
|
{
|
|
uniform._init = false;
|
|
|
|
if (uniform.value !== null)
|
|
{
|
|
this.initSampler2D(uniform);
|
|
}
|
|
}
|
|
else if (type === 'mat2' || type === 'mat3' || type === 'mat4')
|
|
{
|
|
// These require special handling
|
|
uniform.glMatrix = true;
|
|
uniform.glValueLength = 1;
|
|
|
|
if (type === 'mat2')
|
|
{
|
|
uniform.glFunc = gl.uniformMatrix2fv;
|
|
}
|
|
else if (type === 'mat3')
|
|
{
|
|
uniform.glFunc = gl.uniformMatrix3fv;
|
|
}
|
|
else if (type === 'mat4')
|
|
{
|
|
uniform.glFunc = gl.uniformMatrix4fv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// GL function reference
|
|
uniform.glFunc = gl['uniform' + type];
|
|
|
|
if (type === '2f' || type === '2i')
|
|
{
|
|
uniform.glValueLength = 2;
|
|
}
|
|
else if (type === '3f' || type === '3i')
|
|
{
|
|
uniform.glValueLength = 3;
|
|
}
|
|
else if (type === '4f' || type === '4i')
|
|
{
|
|
uniform.glValueLength = 4;
|
|
}
|
|
else
|
|
{
|
|
uniform.glValueLength = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded)
|
|
*
|
|
* @method initSampler2D
|
|
*/
|
|
PIXI.PixiShader.prototype.initSampler2D = function(uniform)
|
|
{
|
|
if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var gl = this.gl;
|
|
|
|
// No need to do string manipulation for this.
|
|
gl.activeTexture(gl.TEXTURE0 + this.textureCount);
|
|
gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]);
|
|
|
|
// Extended texture data
|
|
if (uniform.textureData)
|
|
{
|
|
var data = uniform.textureData;
|
|
|
|
// GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D);
|
|
// GLTextureLinear = mag/min linear, wrap clamp
|
|
// GLTextureNearestRepeat = mag/min NEAREST, wrap repeat
|
|
// GLTextureNearest = mag/min nearest, wrap clamp
|
|
// AudioTexture = whatever + luminance + width 512, height 2, border 0
|
|
// KeyTexture = whatever + luminance + width 256, height 2, border 0
|
|
|
|
// magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST
|
|
// wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT
|
|
|
|
var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR;
|
|
var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR;
|
|
var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE;
|
|
var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE;
|
|
var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA;
|
|
|
|
if (data.repeat)
|
|
{
|
|
wrapS = gl.REPEAT;
|
|
wrapT = gl.REPEAT;
|
|
}
|
|
|
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);
|
|
|
|
if (data.width)
|
|
{
|
|
var width = (data.width) ? data.width : 512;
|
|
var height = (data.height) ? data.height : 2;
|
|
var border = (data.border) ? data.border : 0;
|
|
|
|
// void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null);
|
|
}
|
|
else
|
|
{
|
|
// void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source);
|
|
}
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);
|
|
}
|
|
|
|
gl.uniform1i(uniform.uniformLocation, this.textureCount);
|
|
|
|
uniform._init = true;
|
|
|
|
this.textureCount++;
|
|
|
|
};
|
|
|
|
/**
|
|
* Updates the shader uniform values.
|
|
*
|
|
* @method syncUniforms
|
|
*/
|
|
PIXI.PixiShader.prototype.syncUniforms = function()
|
|
{
|
|
this.textureCount = 1;
|
|
var uniform;
|
|
var gl = this.gl;
|
|
|
|
// This would probably be faster in an array and it would guarantee key order
|
|
for (var key in this.uniforms)
|
|
{
|
|
uniform = this.uniforms[key];
|
|
if (uniform.glValueLength === 1)
|
|
{
|
|
if (uniform.glMatrix === true)
|
|
{
|
|
uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value);
|
|
}
|
|
else
|
|
{
|
|
uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value);
|
|
}
|
|
}
|
|
else if (uniform.glValueLength === 2)
|
|
{
|
|
uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y);
|
|
}
|
|
else if (uniform.glValueLength === 3)
|
|
{
|
|
uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z);
|
|
}
|
|
else if (uniform.glValueLength === 4)
|
|
{
|
|
uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w);
|
|
}
|
|
else if (uniform.type === 'sampler2D')
|
|
{
|
|
if (uniform._init)
|
|
{
|
|
gl.activeTexture(gl['TEXTURE' + this.textureCount]);
|
|
|
|
if(uniform.value.baseTexture._dirty[gl.id])
|
|
{
|
|
PIXI.instances[gl.id].updateTexture(uniform.value.baseTexture);
|
|
}
|
|
else
|
|
{
|
|
// bind the current texture
|
|
gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]);
|
|
}
|
|
|
|
// gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl));
|
|
gl.uniform1i(uniform.uniformLocation, this.textureCount);
|
|
this.textureCount++;
|
|
}
|
|
else
|
|
{
|
|
this.initSampler2D(uniform);
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Destroys the shader.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.PixiShader.prototype.destroy = function()
|
|
{
|
|
this.gl.deleteProgram( this.program );
|
|
this.uniforms = null;
|
|
this.gl = null;
|
|
|
|
this.attributes = null;
|
|
};
|
|
|
|
/**
|
|
* The Default Vertex shader source.
|
|
*
|
|
* @property defaultVertexSrc
|
|
* @type String
|
|
*/
|
|
PIXI.PixiShader.defaultVertexSrc = [
|
|
'// PixiShader Vertex Shader',
|
|
'// With multi-texture rendering',
|
|
'attribute vec2 aVertexPosition;',
|
|
'attribute vec2 aTextureCoord;',
|
|
'attribute vec4 aColor;',
|
|
'attribute float aTextureIndex;',
|
|
|
|
'uniform vec2 projectionVector;',
|
|
'uniform vec2 offsetVector;',
|
|
|
|
'varying vec2 vTextureCoord;',
|
|
'varying vec4 vColor;',
|
|
'varying float vTextureIndex;',
|
|
|
|
'const vec2 center = vec2(-1.0, 1.0);',
|
|
|
|
'void main(void) {',
|
|
' if (aTextureIndex > 0.0) gl_Position = vec4(0.0);',
|
|
' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);',
|
|
' vTextureCoord = aTextureCoord;',
|
|
' vColor = vec4(aColor.rgb * aColor.a, aColor.a);',
|
|
' vTextureIndex = aTextureIndex;',
|
|
'}'
|
|
];
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class PixiFastShader
|
|
* @constructor
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.PixiFastShader = function (gl) {
|
|
/**
|
|
* @property _UID
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._UID = PIXI._UID++;
|
|
|
|
/**
|
|
* @property gl
|
|
* @type WebGLContext
|
|
*/
|
|
this.gl = gl;
|
|
|
|
/**
|
|
* The WebGL program.
|
|
* @property program
|
|
* @type Any
|
|
*/
|
|
this.program = null;
|
|
|
|
if (PIXI._enableMultiTextureToggle) {
|
|
var gl = this.gl;
|
|
this.MAX_TEXTURES = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;\n'
|
|
for (var index = 1; index < this.MAX_TEXTURES; ++index)
|
|
{
|
|
dynamicIfs += '\telse if (vTextureIndex == ' +
|
|
index + '.0) gl_FragColor = texture2D(uSamplerArray[' +
|
|
index + '], vTextureCoord) * vColor;\n'
|
|
}
|
|
|
|
/**
|
|
* The fragment shader.
|
|
* @property fragmentSrc
|
|
* @type Array
|
|
*/
|
|
this.fragmentSrc = [
|
|
'// PixiFastShader Fragment Shader.',
|
|
'precision lowp float;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying float vColor;',
|
|
'varying float vTextureIndex;',
|
|
'uniform sampler2D uSamplerArray[' + this.MAX_TEXTURES + '];',
|
|
'const vec4 PINK = vec4(1.0, 0.0, 1.0, 1.0);',
|
|
'const vec4 GREEN = vec4(0.0, 1.0, 0.0, 1.0);',
|
|
'void main(void) {',
|
|
dynamicIfs,
|
|
'else gl_FragColor = PINK;',
|
|
'}'
|
|
];
|
|
} else {
|
|
this.fragmentSrc = [
|
|
'// PixiFastShader Fragment Shader.',
|
|
'precision lowp float;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying float vColor;',
|
|
'varying float vTextureIndex;',
|
|
'uniform sampler2D uSampler;',
|
|
'void main(void) {',
|
|
' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;',
|
|
'}'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* The vertex shader.
|
|
* @property vertexSrc
|
|
* @type Array
|
|
*/
|
|
this.vertexSrc = [
|
|
'// PixiFastShader Vertex Shader.',
|
|
'attribute vec2 aVertexPosition;',
|
|
'attribute vec2 aPositionCoord;',
|
|
'attribute vec2 aScale;',
|
|
'attribute float aRotation;',
|
|
'attribute vec2 aTextureCoord;',
|
|
'attribute float aColor;',
|
|
'attribute float aTextureIndex;',
|
|
|
|
'uniform vec2 projectionVector;',
|
|
'uniform vec2 offsetVector;',
|
|
'uniform mat3 uMatrix;',
|
|
|
|
'varying vec2 vTextureCoord;',
|
|
'varying float vColor;',
|
|
'varying float vTextureIndex;',
|
|
|
|
'const vec2 center = vec2(-1.0, 1.0);',
|
|
|
|
'void main(void) {',
|
|
' vec2 v;',
|
|
' vec2 sv = aVertexPosition * aScale;',
|
|
' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);',
|
|
' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);',
|
|
' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;',
|
|
' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);',
|
|
' vTextureCoord = aTextureCoord;',
|
|
' vTextureIndex = aTextureIndex;',
|
|
// ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;',
|
|
' vColor = aColor;',
|
|
'}'
|
|
];
|
|
|
|
/**
|
|
* A local texture counter for multi-texture shaders.
|
|
* @property textureCount
|
|
* @type Number
|
|
*/
|
|
this.textureCount = 0;
|
|
|
|
this.init();
|
|
};
|
|
|
|
PIXI.PixiFastShader.prototype.constructor = PIXI.PixiFastShader;
|
|
|
|
/**
|
|
* Initialises the shader.
|
|
*
|
|
* @method init
|
|
*/
|
|
PIXI.PixiFastShader.prototype.init = function () {
|
|
|
|
var gl = this.gl;
|
|
var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
|
|
|
|
gl.useProgram(program);
|
|
|
|
// get and store the uniforms for the shader
|
|
this.uSampler = PIXI._enableMultiTextureToggle ?
|
|
gl.getUniformLocation(program, 'uSamplerArray[0]') :
|
|
gl.getUniformLocation(program, 'uSampler');
|
|
|
|
if (PIXI._enableMultiTextureToggle) {
|
|
var indices = [];
|
|
// HACK: we bind an empty texture to avoid WebGL warning spam.
|
|
var tempTexture = gl.createTexture();
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, tempTexture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
|
|
for (var i = 0; i < this.MAX_TEXTURES; ++i) {
|
|
gl.activeTexture(gl.TEXTURE0 + i);
|
|
gl.bindTexture(gl.TEXTURE_2D, tempTexture);
|
|
indices.push(i);
|
|
}
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.uniform1iv(this.uSampler, indices);
|
|
}
|
|
|
|
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
|
|
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
|
|
this.dimensions = gl.getUniformLocation(program, 'dimensions');
|
|
this.uMatrix = gl.getUniformLocation(program, 'uMatrix');
|
|
|
|
// get and store the attributes
|
|
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
|
|
this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord');
|
|
|
|
this.aScale = gl.getAttribLocation(program, 'aScale');
|
|
this.aRotation = gl.getAttribLocation(program, 'aRotation');
|
|
|
|
this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
|
|
this.colorAttribute = gl.getAttribLocation(program, 'aColor');
|
|
|
|
this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex');
|
|
|
|
// Begin worst hack eva //
|
|
|
|
// WHY??? ONLY on my chrome pixel the line above returns -1 when using filters?
|
|
// maybe its somthing to do with the current state of the gl context.
|
|
// Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel
|
|
// If theres any webGL people that know why could happen please help :)
|
|
if (this.colorAttribute === -1) {
|
|
this.colorAttribute = 2;
|
|
}
|
|
|
|
this.attributes = [
|
|
this.aVertexPosition,
|
|
this.aPositionCoord,
|
|
this.aScale,
|
|
this.aRotation,
|
|
this.aTextureCoord,
|
|
this.colorAttribute,
|
|
this.aTextureIndex
|
|
];
|
|
|
|
// End worst hack eva //
|
|
|
|
this.program = program;
|
|
};
|
|
|
|
/**
|
|
* Destroys the shader.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.PixiFastShader.prototype.destroy = function () {
|
|
this.gl.deleteProgram(this.program);
|
|
this.uniforms = null;
|
|
this.gl = null;
|
|
|
|
this.attributes = null;
|
|
};
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class StripShader
|
|
* @constructor
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.StripShader = function(gl)
|
|
{
|
|
/**
|
|
* @property _UID
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._UID = PIXI._UID++;
|
|
|
|
/**
|
|
* @property gl
|
|
* @type WebGLContext
|
|
*/
|
|
this.gl = gl;
|
|
|
|
/**
|
|
* The WebGL program.
|
|
* @property program
|
|
* @type Any
|
|
*/
|
|
this.program = null;
|
|
|
|
if (PIXI._enableMultiTextureToggle) {
|
|
var gl = this.gl;
|
|
this.MAX_TEXTURES = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord);\n'
|
|
for (var index = 1; index < this.MAX_TEXTURES; ++index)
|
|
{
|
|
dynamicIfs += '\telse if (vTextureIndex == ' +
|
|
index + '.0) gl_FragColor = texture2D(uSamplerArray[' +
|
|
index + '], vTextureCoord) ;\n'
|
|
}
|
|
|
|
|
|
/**
|
|
* The fragment shader.
|
|
* @property fragmentSrc
|
|
* @type Array
|
|
*/
|
|
this.fragmentSrc = [
|
|
'//StripShader Fragment Shader.',
|
|
'precision mediump float;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying float vTextureIndex;',
|
|
// 'varying float vColor;',
|
|
'uniform float alpha;',
|
|
'uniform sampler2D uSamplerArray[' + this.MAX_TEXTURES + '];',
|
|
'const vec4 PINK = vec4(1.0, 0.0, 1.0, 1.0);',
|
|
'const vec4 GREEN = vec4(0.0, 1.0, 0.0, 1.0);',
|
|
'void main(void) {',
|
|
dynamicIfs,
|
|
'else gl_FragColor = PINK;',
|
|
'}'
|
|
];
|
|
} else {
|
|
/**
|
|
* The fragment shader.
|
|
* @property fragmentSrc
|
|
* @type Array
|
|
*/
|
|
this.fragmentSrc = [
|
|
'//StripShader Fragment Shader.',
|
|
'precision mediump float;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying float vTextureIndex;',
|
|
// 'varying float vColor;',
|
|
'uniform float alpha;',
|
|
'uniform sampler2D uSampler;',
|
|
'void main(void) {',
|
|
' gl_FragColor = texture2D(uSampler, vTextureCoord);',
|
|
'}'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* The vertex shader.
|
|
* @property vertexSrc
|
|
* @type Array
|
|
*/
|
|
this.vertexSrc = [
|
|
'//StripShader Vertex Shader.',
|
|
'attribute vec2 aVertexPosition;',
|
|
'attribute vec2 aTextureCoord;',
|
|
'attribute float aTextureIndex;',
|
|
'uniform mat3 translationMatrix;',
|
|
'uniform vec2 projectionVector;',
|
|
'uniform vec2 offsetVector;',
|
|
// 'uniform float alpha;',
|
|
// 'uniform vec3 tint;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying float vTextureIndex;',
|
|
// 'varying vec4 vColor;',
|
|
|
|
'void main(void) {',
|
|
' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);',
|
|
' v -= offsetVector.xyx;',
|
|
' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);',
|
|
' vTextureCoord = aTextureCoord;',
|
|
' vTextureIndex = aTextureIndex;',
|
|
// ' vColor = aColor * vec4(tint * alpha, alpha);',
|
|
'}'
|
|
];
|
|
|
|
this.init();
|
|
};
|
|
|
|
PIXI.StripShader.prototype.constructor = PIXI.StripShader;
|
|
|
|
/**
|
|
* Initialises the shader.
|
|
*
|
|
* @method init
|
|
*/
|
|
PIXI.StripShader.prototype.init = function()
|
|
{
|
|
var gl = this.gl;
|
|
var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
|
|
gl.useProgram(program);
|
|
|
|
// get and store the uniforms for the shader
|
|
this.uSampler = PIXI._enableMultiTextureToggle ?
|
|
gl.getUniformLocation(program, 'uSamplerArray[0]') :
|
|
gl.getUniformLocation(program, 'uSampler');
|
|
|
|
|
|
if (PIXI._enableMultiTextureToggle) {
|
|
var indices = [];
|
|
// HACK: we bind an empty texture to avoid WebGL warning spam.
|
|
var tempTexture = gl.createTexture();
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, tempTexture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
|
|
for (var i = 0; i < this.MAX_TEXTURES; ++i) {
|
|
gl.activeTexture(gl.TEXTURE0 + i);
|
|
gl.bindTexture(gl.TEXTURE_2D, tempTexture);
|
|
indices.push(i);
|
|
}
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.uniform1iv(this.uSampler, indices);
|
|
}
|
|
|
|
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
|
|
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
|
|
this.colorAttribute = gl.getAttribLocation(program, 'aColor');
|
|
this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex');
|
|
//this.dimensions = gl.getUniformLocation(this.program, 'dimensions');
|
|
|
|
// get and store the attributes
|
|
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
|
|
this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
|
|
|
|
this.attributes = [this.aVertexPosition, this.aTextureCoord, this.aTextureIndex];
|
|
|
|
this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix');
|
|
this.alpha = gl.getUniformLocation(program, 'alpha');
|
|
|
|
this.program = program;
|
|
};
|
|
|
|
/**
|
|
* Destroys the shader.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.StripShader.prototype.destroy = function()
|
|
{
|
|
this.gl.deleteProgram( this.program );
|
|
this.uniforms = null;
|
|
this.gl = null;
|
|
|
|
this.attribute = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class PrimitiveShader
|
|
* @constructor
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.PrimitiveShader = function(gl)
|
|
{
|
|
/**
|
|
* @property _UID
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._UID = PIXI._UID++;
|
|
|
|
/**
|
|
* @property gl
|
|
* @type WebGLContext
|
|
*/
|
|
this.gl = gl;
|
|
|
|
/**
|
|
* The WebGL program.
|
|
* @property program
|
|
* @type Any
|
|
*/
|
|
this.program = null;
|
|
|
|
/**
|
|
* The fragment shader.
|
|
* @property fragmentSrc
|
|
* @type Array
|
|
*/
|
|
this.fragmentSrc = [
|
|
'precision mediump float;',
|
|
'varying vec4 vColor;',
|
|
|
|
'void main(void) {',
|
|
' gl_FragColor = vColor;',
|
|
'}'
|
|
];
|
|
|
|
/**
|
|
* The vertex shader.
|
|
* @property vertexSrc
|
|
* @type Array
|
|
*/
|
|
this.vertexSrc = [
|
|
'attribute vec2 aVertexPosition;',
|
|
'attribute vec4 aColor;',
|
|
'uniform mat3 translationMatrix;',
|
|
'uniform vec2 projectionVector;',
|
|
'uniform vec2 offsetVector;',
|
|
'uniform float alpha;',
|
|
'uniform float flipY;',
|
|
'uniform vec3 tint;',
|
|
'varying vec4 vColor;',
|
|
|
|
'void main(void) {',
|
|
' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);',
|
|
' v -= offsetVector.xyx;',
|
|
' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);',
|
|
' vColor = aColor * vec4(tint * alpha, alpha);',
|
|
'}'
|
|
];
|
|
|
|
this.init();
|
|
};
|
|
|
|
PIXI.PrimitiveShader.prototype.constructor = PIXI.PrimitiveShader;
|
|
|
|
/**
|
|
* Initialises the shader.
|
|
*
|
|
* @method init
|
|
*/
|
|
PIXI.PrimitiveShader.prototype.init = function()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
|
|
gl.useProgram(program);
|
|
|
|
// get and store the uniforms for the shader
|
|
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
|
|
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
|
|
this.tintColor = gl.getUniformLocation(program, 'tint');
|
|
this.flipY = gl.getUniformLocation(program, 'flipY');
|
|
|
|
// get and store the attributes
|
|
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
|
|
this.colorAttribute = gl.getAttribLocation(program, 'aColor');
|
|
|
|
this.attributes = [this.aVertexPosition, this.colorAttribute];
|
|
|
|
this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix');
|
|
this.alpha = gl.getUniformLocation(program, 'alpha');
|
|
|
|
this.program = program;
|
|
};
|
|
|
|
/**
|
|
* Destroys the shader.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.PrimitiveShader.prototype.destroy = function()
|
|
{
|
|
this.gl.deleteProgram( this.program );
|
|
this.uniforms = null;
|
|
this.gl = null;
|
|
|
|
this.attributes = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class ComplexPrimitiveShader
|
|
* @constructor
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.ComplexPrimitiveShader = function(gl)
|
|
{
|
|
/**
|
|
* @property _UID
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._UID = PIXI._UID++;
|
|
|
|
/**
|
|
* @property gl
|
|
* @type WebGLContext
|
|
*/
|
|
this.gl = gl;
|
|
|
|
/**
|
|
* The WebGL program.
|
|
* @property program
|
|
* @type Any
|
|
*/
|
|
this.program = null;
|
|
|
|
/**
|
|
* The fragment shader.
|
|
* @property fragmentSrc
|
|
* @type Array
|
|
*/
|
|
this.fragmentSrc = [
|
|
|
|
'precision mediump float;',
|
|
|
|
'varying vec4 vColor;',
|
|
|
|
'void main(void) {',
|
|
' gl_FragColor = vColor;',
|
|
'}'
|
|
];
|
|
|
|
/**
|
|
* The vertex shader.
|
|
* @property vertexSrc
|
|
* @type Array
|
|
*/
|
|
this.vertexSrc = [
|
|
'attribute vec2 aVertexPosition;',
|
|
//'attribute vec4 aColor;',
|
|
'uniform mat3 translationMatrix;',
|
|
'uniform vec2 projectionVector;',
|
|
'uniform vec2 offsetVector;',
|
|
|
|
'uniform vec3 tint;',
|
|
'uniform float alpha;',
|
|
'uniform vec3 color;',
|
|
'uniform float flipY;',
|
|
'varying vec4 vColor;',
|
|
|
|
'void main(void) {',
|
|
' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);',
|
|
' v -= offsetVector.xyx;',
|
|
' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);',
|
|
' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);',
|
|
'}'
|
|
];
|
|
|
|
this.init();
|
|
};
|
|
|
|
PIXI.ComplexPrimitiveShader.prototype.constructor = PIXI.ComplexPrimitiveShader;
|
|
|
|
/**
|
|
* Initialises the shader.
|
|
*
|
|
* @method init
|
|
*/
|
|
PIXI.ComplexPrimitiveShader.prototype.init = function()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
|
|
gl.useProgram(program);
|
|
|
|
// get and store the uniforms for the shader
|
|
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
|
|
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
|
|
this.tintColor = gl.getUniformLocation(program, 'tint');
|
|
this.color = gl.getUniformLocation(program, 'color');
|
|
this.flipY = gl.getUniformLocation(program, 'flipY');
|
|
|
|
// get and store the attributes
|
|
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
|
|
// this.colorAttribute = gl.getAttribLocation(program, 'aColor');
|
|
|
|
this.attributes = [this.aVertexPosition, this.colorAttribute];
|
|
|
|
this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix');
|
|
this.alpha = gl.getUniformLocation(program, 'alpha');
|
|
|
|
this.program = program;
|
|
};
|
|
|
|
/**
|
|
* Destroys the shader.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.ComplexPrimitiveShader.prototype.destroy = function()
|
|
{
|
|
this.gl.deleteProgram( this.program );
|
|
this.uniforms = null;
|
|
this.gl = null;
|
|
|
|
this.attribute = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
PIXI.glContexts = []; // this is where we store the webGL contexts for easy access.
|
|
PIXI.instances = [];
|
|
PIXI._enableMultiTextureToggle = false;
|
|
|
|
/**
|
|
* The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer
|
|
* should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs.
|
|
* So no need for Sprite Batches or Sprite Clouds.
|
|
* Don't forget to add the view to your DOM or you will not see anything :)
|
|
*
|
|
* @class WebGLRenderer
|
|
* @constructor
|
|
* @param game {Phaser.Game} A reference to the Phaser Game instance
|
|
*/
|
|
PIXI.WebGLRenderer = function(game) {
|
|
|
|
/**
|
|
* @property {Phaser.Game} game - A reference to the Phaser Game instance.
|
|
*/
|
|
this.game = game;
|
|
|
|
if (!PIXI.defaultRenderer)
|
|
{
|
|
PIXI.defaultRenderer = this;
|
|
}
|
|
|
|
this.extensions = {};
|
|
|
|
/**
|
|
* @property type
|
|
* @type Number
|
|
*/
|
|
this.type = PIXI.WEBGL_RENDERER;
|
|
|
|
/**
|
|
* The resolution of the renderer
|
|
*
|
|
* @property resolution
|
|
* @type Number
|
|
* @default 1
|
|
*/
|
|
this.resolution = game.resolution;
|
|
|
|
/**
|
|
* Whether the render view is transparent
|
|
*
|
|
* @property transparent
|
|
* @type Boolean
|
|
*/
|
|
this.transparent = game.transparent;
|
|
|
|
/**
|
|
* Whether the render view should be resized automatically
|
|
*
|
|
* @property autoResize
|
|
* @type Boolean
|
|
*/
|
|
this.autoResize = false;
|
|
|
|
/**
|
|
* The value of the preserveDrawingBuffer flag affects whether or not the contents of the stencil buffer is retained after rendering.
|
|
*
|
|
* @property preserveDrawingBuffer
|
|
* @type Boolean
|
|
*/
|
|
this.preserveDrawingBuffer = game.preserveDrawingBuffer;
|
|
|
|
/**
|
|
* This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true:
|
|
* If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0).
|
|
* If the Stage is transparent, Pixi will clear to the target Stage's background color.
|
|
* Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set.
|
|
*
|
|
* @property clearBeforeRender
|
|
* @type Boolean
|
|
* @default
|
|
*/
|
|
this.clearBeforeRender = game.clearBeforeRender;
|
|
|
|
/**
|
|
* The width of the canvas view
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
this.width = game.width;
|
|
|
|
/**
|
|
* The height of the canvas view
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
this.height = game.height;
|
|
|
|
/**
|
|
* The canvas element that everything is drawn to
|
|
*
|
|
* @property view
|
|
* @type HTMLCanvasElement
|
|
*/
|
|
this.view = game.canvas;
|
|
|
|
/**
|
|
* @property _contextOptions
|
|
* @type Object
|
|
* @private
|
|
*/
|
|
this._contextOptions = {
|
|
alpha: this.transparent,
|
|
antialias: game.antialias,
|
|
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
|
|
stencil: true,
|
|
preserveDrawingBuffer: this.preserveDrawingBuffer
|
|
};
|
|
|
|
/**
|
|
* @property projection
|
|
* @type Point
|
|
*/
|
|
this.projection = new PIXI.Point();
|
|
|
|
/**
|
|
* @property offset
|
|
* @type Point
|
|
*/
|
|
this.offset = new PIXI.Point();
|
|
|
|
// time to create the render managers! each one focuses on managing a state in webGL
|
|
|
|
/**
|
|
* Deals with managing the shader programs and their attribs
|
|
* @property shaderManager
|
|
* @type WebGLShaderManager
|
|
*/
|
|
this.shaderManager = new PIXI.WebGLShaderManager();
|
|
|
|
/**
|
|
* Manages the rendering of sprites
|
|
* @property spriteBatch
|
|
* @type WebGLSpriteBatch
|
|
*/
|
|
this.spriteBatch = new PIXI.WebGLSpriteBatch();
|
|
|
|
/**
|
|
* Manages the masks using the stencil buffer
|
|
* @property maskManager
|
|
* @type WebGLMaskManager
|
|
*/
|
|
this.maskManager = new PIXI.WebGLMaskManager();
|
|
|
|
/**
|
|
* Manages the filters
|
|
* @property filterManager
|
|
* @type WebGLFilterManager
|
|
*/
|
|
this.filterManager = new PIXI.WebGLFilterManager();
|
|
|
|
/**
|
|
* Manages the stencil buffer
|
|
* @property stencilManager
|
|
* @type WebGLStencilManager
|
|
*/
|
|
this.stencilManager = new PIXI.WebGLStencilManager();
|
|
|
|
/**
|
|
* Manages the blendModes
|
|
* @property blendModeManager
|
|
* @type WebGLBlendModeManager
|
|
*/
|
|
this.blendModeManager = new PIXI.WebGLBlendModeManager();
|
|
|
|
/**
|
|
* @property renderSession
|
|
* @type Object
|
|
*/
|
|
this.renderSession = {};
|
|
|
|
/**
|
|
* @property currentBatchedTextures
|
|
* @type Array
|
|
*/
|
|
this.currentBatchedTextures = [];
|
|
|
|
// Needed?
|
|
this.renderSession.game = this.game;
|
|
this.renderSession.gl = this.gl;
|
|
this.renderSession.drawCount = 0;
|
|
this.renderSession.shaderManager = this.shaderManager;
|
|
this.renderSession.maskManager = this.maskManager;
|
|
this.renderSession.filterManager = this.filterManager;
|
|
this.renderSession.blendModeManager = this.blendModeManager;
|
|
this.renderSession.spriteBatch = this.spriteBatch;
|
|
this.renderSession.stencilManager = this.stencilManager;
|
|
this.renderSession.renderer = this;
|
|
this.renderSession.resolution = this.resolution;
|
|
|
|
// time init the context..
|
|
this.initContext();
|
|
|
|
// map some webGL blend modes..
|
|
this.mapBlendModes();
|
|
|
|
};
|
|
|
|
// constructor
|
|
PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer;
|
|
|
|
/**
|
|
* @method initContext
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.initContext = function()
|
|
{
|
|
var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions);
|
|
|
|
this.gl = gl;
|
|
|
|
if (!gl) {
|
|
// fail, not able to get a context
|
|
throw new Error('This browser does not support webGL. Try using the canvas renderer');
|
|
}
|
|
|
|
this.maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
|
|
this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId++;
|
|
|
|
PIXI.glContexts[this.glContextId] = gl;
|
|
|
|
PIXI.instances[this.glContextId] = this;
|
|
|
|
// set up the default pixi settings..
|
|
gl.disable(gl.DEPTH_TEST);
|
|
gl.disable(gl.CULL_FACE);
|
|
gl.enable(gl.BLEND);
|
|
|
|
// need to set the context for all the managers...
|
|
this.shaderManager.setContext(gl);
|
|
this.spriteBatch.setContext(gl);
|
|
this.maskManager.setContext(gl);
|
|
this.filterManager.setContext(gl);
|
|
this.blendModeManager.setContext(gl);
|
|
this.stencilManager.setContext(gl);
|
|
|
|
this.renderSession.gl = this.gl;
|
|
|
|
// now resize and we are good to go!
|
|
this.resize(this.width, this.height);
|
|
|
|
// Load WebGL extension
|
|
this.extensions.compression = {};
|
|
|
|
etc1 = gl.getExtension('WEBGL_compressed_texture_etc1') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc1');
|
|
pvrtc = gl.getExtension('WEBGL_compressed_texture_pvrtc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
|
|
s3tc = gl.getExtension('WEBGL_compressed_texture_s3tc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
|
|
|
|
if (etc1) this.extensions.compression.ETC1 = etc1;
|
|
if (pvrtc) this.extensions.compression.PVRTC = pvrtc;
|
|
if (s3tc) this.extensions.compression.S3TC = s3tc;
|
|
};
|
|
|
|
/**
|
|
* If Multi Texture support has been enabled, then calling this method will enable batching on the given
|
|
* textures. The texture collection is an array of keys, that map to Phaser.Cache image entries.
|
|
*
|
|
* The number of textures that can be batched is dependent on hardware. If you provide more textures
|
|
* than can be batched by the GPU, then only those at the start of the array will be used. Generally
|
|
* you shouldn't provide more than 16 textures to this method. You can check the hardware limit via the
|
|
* `maxTextures` property.
|
|
*
|
|
* You can also check the property `currentBatchedTextures` at any time, to see which textures are currently
|
|
* being batched.
|
|
*
|
|
* To stop all textures from being batched, call this method again with an empty array.
|
|
*
|
|
* To change the textures being batched, call this method with a new array of image keys. The old ones
|
|
* will all be purged out and no-longer batched, and the new ones enabled.
|
|
*
|
|
* Note: Throws a warning if you haven't enabled Multiple Texture batching support in the Phaser Game config.
|
|
*
|
|
* @method setTexturePriority
|
|
* @param textureNameCollection {Array} An Array of Texture Cache keys to use for multi-texture batching.
|
|
* @return {Array} An array containing the texture keys that were enabled for batching.
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.setTexturePriority = function (textureNameCollection) {
|
|
|
|
if (!PIXI._enableMultiTextureToggle)
|
|
{
|
|
console.warn('setTexturePriority error: Multi Texture support hasn\'t been enabled in the Phaser Game Config.');
|
|
return;
|
|
}
|
|
|
|
var maxTextures = this.maxTextures;
|
|
var imageCache = this.game.cache._cache.image;
|
|
var imageName = null;
|
|
var gl = this.gl;
|
|
|
|
// Clear out all previously batched textures and reset their flags.
|
|
// If the array has been modified, then the developer will have to
|
|
// deal with that in their own way.
|
|
for (var i = 0; i < this.currentBatchedTextures.length; i++)
|
|
{
|
|
imageName = textureNameCollection[index];
|
|
|
|
if (!(imageName in imageCache))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
imageCache[imageName].base.textureIndex = 0;
|
|
}
|
|
|
|
this.currentBatchedTextures.length = 0;
|
|
|
|
// We start from 1 because framebuffer texture uses unit 0.
|
|
for (var index = 0; index < textureNameCollection.length; ++index)
|
|
{
|
|
imageName = textureNameCollection[index];
|
|
|
|
if (!(imageName in imageCache))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (index + 1 < maxTextures)
|
|
{
|
|
imageCache[imageName].base.textureIndex = index + 1;
|
|
}
|
|
else
|
|
{
|
|
imageCache[imageName].base.textureIndex = maxTextures - 1;
|
|
}
|
|
|
|
this.currentBatchedTextures.push(imageName);
|
|
}
|
|
|
|
return this.currentBatchedTextures;
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders the stage to its webGL view
|
|
*
|
|
* @method render
|
|
* @param stage {Stage} the Stage element to be rendered
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.render = function(stage)
|
|
{
|
|
// no point rendering if our context has been blown up!
|
|
if (this.contextLost)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var gl = this.gl;
|
|
|
|
// -- Does this need to be set every frame? -- //
|
|
gl.viewport(0, 0, this.width, this.height);
|
|
|
|
// make sure we are bound to the main frame buffer
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
|
|
if (this.game.clearBeforeRender)
|
|
{
|
|
gl.clearColor(stage._bgColor.r, stage._bgColor.g, stage._bgColor.b, stage._bgColor.a);
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
this.offset.x = this.game.camera._shake.x;
|
|
this.offset.y = this.game.camera._shake.y;
|
|
|
|
this.renderDisplayObject(stage, this.projection);
|
|
};
|
|
|
|
/**
|
|
* Renders a Display Object.
|
|
*
|
|
* @method renderDisplayObject
|
|
* @param displayObject {DisplayObject} The DisplayObject to render
|
|
* @param projection {Point} The projection
|
|
* @param buffer {Array} a standard WebGL buffer
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer, matrix)
|
|
{
|
|
this.renderSession.blendModeManager.setBlendMode(PIXI.blendModes.NORMAL);
|
|
|
|
// reset the render session data..
|
|
this.renderSession.drawCount = 0;
|
|
|
|
// make sure to flip the Y if using a render texture..
|
|
this.renderSession.flipY = buffer ? -1 : 1;
|
|
|
|
// set the default projection
|
|
this.renderSession.projection = projection;
|
|
|
|
//set the default offset
|
|
this.renderSession.offset = this.offset;
|
|
|
|
// start the sprite batch
|
|
this.spriteBatch.begin(this.renderSession);
|
|
|
|
// start the filter manager
|
|
this.filterManager.begin(this.renderSession, buffer);
|
|
|
|
// render the scene!
|
|
displayObject._renderWebGL(this.renderSession, matrix);
|
|
|
|
// finish the sprite batch
|
|
this.spriteBatch.end();
|
|
};
|
|
|
|
/**
|
|
* Resizes the webGL view to the specified width and height.
|
|
*
|
|
* @method resize
|
|
* @param width {Number} the new width of the webGL view
|
|
* @param height {Number} the new height of the webGL view
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.resize = function(width, height)
|
|
{
|
|
this.width = width * this.resolution;
|
|
this.height = height * this.resolution;
|
|
|
|
this.view.width = this.width;
|
|
this.view.height = this.height;
|
|
|
|
if (this.autoResize) {
|
|
this.view.style.width = this.width / this.resolution + 'px';
|
|
this.view.style.height = this.height / this.resolution + 'px';
|
|
}
|
|
|
|
this.gl.viewport(0, 0, this.width, this.height);
|
|
|
|
this.projection.x = this.width / 2 / this.resolution;
|
|
this.projection.y = -this.height / 2 / this.resolution;
|
|
};
|
|
|
|
/**
|
|
* Updates and creates a WebGL compressed texture for the renderers context.
|
|
*
|
|
* @method updateCompressedTexture
|
|
* @param texture {Texture} the texture to update
|
|
* @return {boolean} True if the texture was successfully bound, otherwise false.
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.updateCompressedTexture = function (texture) {
|
|
if (!texture.hasLoaded)
|
|
{
|
|
return false;
|
|
}
|
|
var gl = this.gl;
|
|
var textureMetaData = texture.source;
|
|
|
|
if (!texture._glTextures[gl.id])
|
|
{
|
|
texture._glTextures[gl.id] = gl.createTexture();
|
|
}
|
|
gl.activeTexture(gl.TEXTURE0 + texture.textureIndex);
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]);
|
|
|
|
gl.compressedTexImage2D(
|
|
gl.TEXTURE_2D,
|
|
0,
|
|
textureMetaData.glExtensionFormat,
|
|
textureMetaData.width,
|
|
textureMetaData.height,
|
|
0,
|
|
textureMetaData.textureData
|
|
);
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
|
|
|
|
if (texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height))
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST);
|
|
gl.generateMipmap(gl.TEXTURE_2D);
|
|
}
|
|
else
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
|
|
}
|
|
|
|
if (!texture._powerOf2)
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
}
|
|
else
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
|
}
|
|
texture._dirty[gl.id] = false;
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Updates and Creates a WebGL texture for the renderers context.
|
|
*
|
|
* @method updateTexture
|
|
* @param texture {Texture} the texture to update
|
|
* @return {boolean} True if the texture was successfully bound, otherwise false.
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.updateTexture = function(texture)
|
|
{
|
|
if (!texture.hasLoaded)
|
|
{
|
|
return false;
|
|
}
|
|
if (texture.source.compressionAlgorithm) {
|
|
return this.updateCompressedTexture(texture);
|
|
}
|
|
|
|
var gl = this.gl;
|
|
|
|
if (!texture._glTextures[gl.id])
|
|
{
|
|
texture._glTextures[gl.id] = gl.createTexture();
|
|
}
|
|
gl.activeTexture(gl.TEXTURE0 + texture.textureIndex);
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]);
|
|
|
|
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha);
|
|
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source);
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
|
|
|
|
if (texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height))
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST);
|
|
gl.generateMipmap(gl.TEXTURE_2D);
|
|
}
|
|
else
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
|
|
}
|
|
|
|
if (!texture._powerOf2)
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
}
|
|
else
|
|
{
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
|
}
|
|
|
|
texture._dirty[gl.id] = false;
|
|
|
|
// return texture._glTextures[gl.id];
|
|
return true;
|
|
|
|
};
|
|
|
|
/**
|
|
* Removes everything from the renderer (event listeners, spritebatch, etc...)
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.destroy = function()
|
|
{
|
|
PIXI.glContexts[this.glContextId] = null;
|
|
|
|
this.projection = null;
|
|
this.offset = null;
|
|
|
|
this.shaderManager.destroy();
|
|
this.spriteBatch.destroy();
|
|
this.maskManager.destroy();
|
|
this.filterManager.destroy();
|
|
|
|
this.shaderManager = null;
|
|
this.spriteBatch = null;
|
|
this.maskManager = null;
|
|
this.filterManager = null;
|
|
|
|
this.gl = null;
|
|
this.renderSession = null;
|
|
|
|
PIXI.CanvasPool.remove(this);
|
|
|
|
PIXI.instances[this.glContextId] = null;
|
|
|
|
PIXI.WebGLRenderer.glContextId--;
|
|
};
|
|
|
|
/**
|
|
* Maps Pixi blend modes to WebGL blend modes.
|
|
*
|
|
* @method mapBlendModes
|
|
*/
|
|
PIXI.WebGLRenderer.prototype.mapBlendModes = function()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
if (!PIXI.blendModesWebGL)
|
|
{
|
|
var b = [];
|
|
var modes = PIXI.blendModes;
|
|
|
|
b[modes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA];
|
|
b[modes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.SCREEN] = [gl.SRC_ALPHA, gl.ONE];
|
|
b[modes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
b[modes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
|
|
|
|
PIXI.blendModesWebGL = b;
|
|
}
|
|
};
|
|
|
|
PIXI.WebGLRenderer.prototype.getMaxTextureUnit = function() {
|
|
var gl = this.gl;
|
|
return gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
};
|
|
|
|
PIXI.enableMultiTexture = function() {
|
|
PIXI._enableMultiTextureToggle = true;
|
|
};
|
|
|
|
PIXI.WebGLRenderer.glContextId = 0;
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class WebGLBlendModeManager
|
|
* @constructor
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLBlendModeManager = function()
|
|
{
|
|
/**
|
|
* @property currentBlendMode
|
|
* @type Number
|
|
*/
|
|
this.currentBlendMode = 99999;
|
|
};
|
|
|
|
PIXI.WebGLBlendModeManager.prototype.constructor = PIXI.WebGLBlendModeManager;
|
|
|
|
/**
|
|
* Sets the WebGL Context.
|
|
*
|
|
* @method setContext
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLBlendModeManager.prototype.setContext = function(gl)
|
|
{
|
|
this.gl = gl;
|
|
};
|
|
|
|
/**
|
|
* Sets-up the given blendMode from WebGL's point of view.
|
|
*
|
|
* @method setBlendMode
|
|
* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD
|
|
*/
|
|
PIXI.WebGLBlendModeManager.prototype.setBlendMode = function(blendMode)
|
|
{
|
|
if(this.currentBlendMode === blendMode)return false;
|
|
|
|
this.currentBlendMode = blendMode;
|
|
|
|
var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode];
|
|
|
|
if (blendModeWebGL)
|
|
{
|
|
this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Destroys this object.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.WebGLBlendModeManager.prototype.destroy = function()
|
|
{
|
|
this.gl = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class WebGLMaskManager
|
|
* @constructor
|
|
* @private
|
|
*/
|
|
PIXI.WebGLMaskManager = function()
|
|
{
|
|
};
|
|
|
|
PIXI.WebGLMaskManager.prototype.constructor = PIXI.WebGLMaskManager;
|
|
|
|
/**
|
|
* Sets the drawing context to the one given in parameter.
|
|
*
|
|
* @method setContext
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLMaskManager.prototype.setContext = function(gl)
|
|
{
|
|
this.gl = gl;
|
|
};
|
|
|
|
/**
|
|
* Applies the Mask and adds it to the current filter stack.
|
|
*
|
|
* @method pushMask
|
|
* @param maskData {Array}
|
|
* @param renderSession {Object}
|
|
*/
|
|
PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession)
|
|
{
|
|
var gl = renderSession.gl;
|
|
|
|
if (maskData.dirty)
|
|
{
|
|
PIXI.WebGLGraphics.updateGraphics(maskData, gl);
|
|
}
|
|
|
|
if (maskData._webGL[gl.id] === undefined || maskData._webGL[gl.id].data === undefined || maskData._webGL[gl.id].data.length === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession);
|
|
};
|
|
|
|
/**
|
|
* Removes the last filter from the filter stack and doesn't return it.
|
|
*
|
|
* @method popMask
|
|
* @param maskData {Array}
|
|
* @param renderSession {Object} an object containing all the useful parameters
|
|
*/
|
|
PIXI.WebGLMaskManager.prototype.popMask = function(maskData, renderSession)
|
|
{
|
|
var gl = this.gl;
|
|
|
|
if (maskData._webGL[gl.id] === undefined || maskData._webGL[gl.id].data === undefined || maskData._webGL[gl.id].data.length === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession);
|
|
|
|
};
|
|
|
|
/**
|
|
* Destroys the mask stack.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.WebGLMaskManager.prototype.destroy = function()
|
|
{
|
|
this.gl = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class WebGLStencilManager
|
|
* @constructor
|
|
* @private
|
|
*/
|
|
PIXI.WebGLStencilManager = function()
|
|
{
|
|
this.stencilStack = [];
|
|
this.reverse = true;
|
|
this.count = 0;
|
|
};
|
|
|
|
/**
|
|
* Sets the drawing context to the one given in parameter.
|
|
*
|
|
* @method setContext
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLStencilManager.prototype.setContext = function(gl)
|
|
{
|
|
this.gl = gl;
|
|
};
|
|
|
|
/**
|
|
* Applies the Mask and adds it to the current filter stack.
|
|
*
|
|
* @method pushMask
|
|
* @param graphics {Graphics}
|
|
* @param webGLData {Array}
|
|
* @param renderSession {Object}
|
|
*/
|
|
PIXI.WebGLStencilManager.prototype.pushStencil = function(graphics, webGLData, renderSession)
|
|
{
|
|
var gl = this.gl;
|
|
this.bindGraphics(graphics, webGLData, renderSession);
|
|
|
|
if(this.stencilStack.length === 0)
|
|
{
|
|
gl.enable(gl.STENCIL_TEST);
|
|
gl.clear(gl.STENCIL_BUFFER_BIT);
|
|
this.reverse = true;
|
|
this.count = 0;
|
|
}
|
|
|
|
this.stencilStack.push(webGLData);
|
|
|
|
var level = this.count;
|
|
|
|
gl.colorMask(false, false, false, false);
|
|
|
|
gl.stencilFunc(gl.ALWAYS,0,0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT);
|
|
|
|
// draw the triangle strip!
|
|
|
|
if(webGLData.mode === 1)
|
|
{
|
|
gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
if(this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level, 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR);
|
|
}
|
|
|
|
// draw a quad to increment..
|
|
gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 );
|
|
|
|
if(this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level+1, 0xFF);
|
|
}
|
|
|
|
this.reverse = !this.reverse;
|
|
}
|
|
else
|
|
{
|
|
if(!this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level, 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR);
|
|
}
|
|
|
|
gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
if(!this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level+1, 0xFF);
|
|
}
|
|
}
|
|
|
|
gl.colorMask(true, true, true, true);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
|
|
|
|
this.count++;
|
|
};
|
|
|
|
/**
|
|
* TODO this does not belong here!
|
|
*
|
|
* @method bindGraphics
|
|
* @param graphics {Graphics}
|
|
* @param webGLData {Array}
|
|
* @param renderSession {Object}
|
|
*/
|
|
PIXI.WebGLStencilManager.prototype.bindGraphics = function(graphics, webGLData, renderSession)
|
|
{
|
|
//if(this._currentGraphics === graphics)return;
|
|
this._currentGraphics = graphics;
|
|
|
|
var gl = this.gl;
|
|
|
|
// bind the graphics object..
|
|
var projection = renderSession.projection,
|
|
offset = renderSession.offset,
|
|
shader;// = renderSession.shaderManager.primitiveShader;
|
|
|
|
if(webGLData.mode === 1)
|
|
{
|
|
shader = renderSession.shaderManager.complexPrimitiveShader;
|
|
|
|
renderSession.shaderManager.setShader( shader );
|
|
|
|
gl.uniform1f(shader.flipY, renderSession.flipY);
|
|
|
|
gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true));
|
|
|
|
gl.uniform2f(shader.projectionVector, projection.x, -projection.y);
|
|
gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);
|
|
|
|
gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint));
|
|
gl.uniform3fv(shader.color, webGLData.color);
|
|
|
|
gl.uniform1f(shader.alpha, graphics.worldAlpha * webGLData.alpha);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer);
|
|
|
|
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 2, 0);
|
|
|
|
|
|
// now do the rest..
|
|
// set the index buffer!
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer);
|
|
}
|
|
else
|
|
{
|
|
//renderSession.shaderManager.activatePrimitiveShader();
|
|
shader = renderSession.shaderManager.primitiveShader;
|
|
renderSession.shaderManager.setShader( shader );
|
|
|
|
gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true));
|
|
|
|
gl.uniform1f(shader.flipY, renderSession.flipY);
|
|
gl.uniform2f(shader.projectionVector, projection.x, -projection.y);
|
|
gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);
|
|
|
|
gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint));
|
|
|
|
gl.uniform1f(shader.alpha, graphics.worldAlpha);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer);
|
|
|
|
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0);
|
|
gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4);
|
|
|
|
// set the index buffer!
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method popStencil
|
|
* @param graphics {Graphics}
|
|
* @param webGLData {Array}
|
|
* @param renderSession {Object}
|
|
*/
|
|
PIXI.WebGLStencilManager.prototype.popStencil = function(graphics, webGLData, renderSession)
|
|
{
|
|
var gl = this.gl;
|
|
this.stencilStack.pop();
|
|
|
|
this.count--;
|
|
|
|
if(this.stencilStack.length === 0)
|
|
{
|
|
// the stack is empty!
|
|
gl.disable(gl.STENCIL_TEST);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
var level = this.count;
|
|
|
|
this.bindGraphics(graphics, webGLData, renderSession);
|
|
|
|
gl.colorMask(false, false, false, false);
|
|
|
|
if(webGLData.mode === 1)
|
|
{
|
|
this.reverse = !this.reverse;
|
|
|
|
if(this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level+1, 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR);
|
|
}
|
|
|
|
// draw a quad to increment..
|
|
gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 );
|
|
|
|
gl.stencilFunc(gl.ALWAYS,0,0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT);
|
|
|
|
// draw the triangle strip!
|
|
gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
if(!this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level, 0xFF);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// console.log("<<>>")
|
|
if(!this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level+1, 0xFF);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR);
|
|
}
|
|
|
|
gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
if(!this.reverse)
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF);
|
|
}
|
|
else
|
|
{
|
|
gl.stencilFunc(gl.EQUAL,level, 0xFF);
|
|
}
|
|
}
|
|
|
|
gl.colorMask(true, true, true, true);
|
|
gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
|
|
|
|
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Destroys the mask stack.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.WebGLStencilManager.prototype.destroy = function()
|
|
{
|
|
this.stencilStack = null;
|
|
this.gl = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class WebGLShaderManager
|
|
* @constructor
|
|
* @private
|
|
*/
|
|
PIXI.WebGLShaderManager = function()
|
|
{
|
|
/**
|
|
* @property maxAttibs
|
|
* @type Number
|
|
*/
|
|
this.maxAttibs = 10;
|
|
|
|
/**
|
|
* @property attribState
|
|
* @type Array
|
|
*/
|
|
this.attribState = [];
|
|
|
|
/**
|
|
* @property tempAttribState
|
|
* @type Array
|
|
*/
|
|
this.tempAttribState = [];
|
|
|
|
for (var i = 0; i < this.maxAttibs; i++)
|
|
{
|
|
this.attribState[i] = false;
|
|
}
|
|
|
|
/**
|
|
* @property stack
|
|
* @type Array
|
|
*/
|
|
this.stack = [];
|
|
|
|
};
|
|
|
|
PIXI.WebGLShaderManager.prototype.constructor = PIXI.WebGLShaderManager;
|
|
|
|
/**
|
|
* Initialises the context and the properties.
|
|
*
|
|
* @method setContext
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLShaderManager.prototype.setContext = function(gl)
|
|
{
|
|
this.gl = gl;
|
|
|
|
// the next one is used for rendering primitives
|
|
this.primitiveShader = new PIXI.PrimitiveShader(gl);
|
|
|
|
// the next one is used for rendering triangle strips
|
|
this.complexPrimitiveShader = new PIXI.ComplexPrimitiveShader(gl);
|
|
|
|
// this shader is used for the default sprite rendering
|
|
this.defaultShader = new PIXI.PixiShader(gl);
|
|
|
|
// this shader is used for the fast sprite rendering
|
|
this.fastShader = new PIXI.PixiFastShader(gl);
|
|
|
|
// the next one is used for rendering triangle strips
|
|
this.stripShader = new PIXI.StripShader(gl);
|
|
|
|
this.setShader(this.defaultShader);
|
|
};
|
|
|
|
/**
|
|
* Takes the attributes given in parameters.
|
|
*
|
|
* @method setAttribs
|
|
* @param attribs {Array} attribs
|
|
*/
|
|
PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs)
|
|
{
|
|
// reset temp state
|
|
var i;
|
|
|
|
for (i = 0; i < this.tempAttribState.length; i++)
|
|
{
|
|
this.tempAttribState[i] = false;
|
|
}
|
|
|
|
// set the new attribs
|
|
for (i = 0; i < attribs.length; i++)
|
|
{
|
|
var attribId = attribs[i];
|
|
this.tempAttribState[attribId] = true;
|
|
}
|
|
|
|
var gl = this.gl;
|
|
|
|
for (i = 0; i < this.attribState.length; i++)
|
|
{
|
|
if(this.attribState[i] !== this.tempAttribState[i])
|
|
{
|
|
this.attribState[i] = this.tempAttribState[i];
|
|
|
|
if(this.tempAttribState[i])
|
|
{
|
|
gl.enableVertexAttribArray(i);
|
|
}
|
|
else
|
|
{
|
|
gl.disableVertexAttribArray(i);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sets the current shader.
|
|
*
|
|
* @method setShader
|
|
* @param shader {Any}
|
|
*/
|
|
PIXI.WebGLShaderManager.prototype.setShader = function(shader)
|
|
{
|
|
if(this._currentId === shader._UID)return false;
|
|
|
|
this._currentId = shader._UID;
|
|
|
|
this.currentShader = shader;
|
|
|
|
this.gl.useProgram(shader.program);
|
|
this.setAttribs(shader.attributes);
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Destroys this object.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.WebGLShaderManager.prototype.destroy = function()
|
|
{
|
|
this.attribState = null;
|
|
|
|
this.tempAttribState = null;
|
|
|
|
this.primitiveShader.destroy();
|
|
|
|
this.complexPrimitiveShader.destroy();
|
|
|
|
this.defaultShader.destroy();
|
|
|
|
this.fastShader.destroy();
|
|
|
|
this.stripShader.destroy();
|
|
|
|
this.gl = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves
|
|
*
|
|
* Big thanks to the very clever Matt DesLauriers <mattdesl> https://github.com/mattdesl/
|
|
* for creating the original pixi version!
|
|
* Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer
|
|
*
|
|
* Heavily inspired by LibGDX's WebGLSpriteBatch:
|
|
* https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @class WebGLSpriteBatch
|
|
* @private
|
|
* @constructor
|
|
*/
|
|
PIXI.WebGLSpriteBatch = function () {
|
|
/**
|
|
* @property vertSize
|
|
* @type Number
|
|
*/
|
|
this.vertSize = 5;
|
|
|
|
/**
|
|
* The number of images in the SpriteBatch before it flushes
|
|
* @property size
|
|
* @type Number
|
|
*/
|
|
this.size = 2000; //Math.pow(2, 16) / this.vertSize;
|
|
|
|
//the total number of bytes in our batch
|
|
// Including texture index:
|
|
// position + uv + color + textureIndex
|
|
// vec2 + vec2 + (char * 4) + float
|
|
this.vertexSize = (4 * 2) + (4 * 2) + (4) + (4);
|
|
var numVerts = this.vertexSize * this.size * 4;
|
|
//this.size * 4 * 4 * this.vertSize;
|
|
//the total number of indices in our batch
|
|
var numIndices = this.size * 6;
|
|
|
|
/**
|
|
* Holds the vertices
|
|
*
|
|
* @property vertices
|
|
* @type ArrayBuffer
|
|
*/
|
|
this.vertices = new PIXI.ArrayBuffer(numVerts);
|
|
|
|
/**
|
|
* View on the vertices as a Float32Array
|
|
*
|
|
* @property positions
|
|
* @type Float32Array
|
|
*/
|
|
this.positions = new PIXI.Float32Array(this.vertices);
|
|
|
|
/**
|
|
* View on the vertices as a Uint32Array
|
|
*
|
|
* @property colors
|
|
* @type Uint32Array
|
|
*/
|
|
this.colors = new PIXI.Uint32Array(this.vertices);
|
|
|
|
/**
|
|
* Holds the indices
|
|
*
|
|
* @property indices
|
|
* @type Uint16Array
|
|
*/
|
|
this.indices = new PIXI.Uint16Array(numIndices);
|
|
|
|
/**
|
|
* @property lastIndexCount
|
|
* @type Number
|
|
*/
|
|
this.lastIndexCount = 0;
|
|
|
|
for (var i = 0, j = 0; i < numIndices; i += 6, j += 4) {
|
|
this.indices[i + 0] = j + 0;
|
|
this.indices[i + 1] = j + 1;
|
|
this.indices[i + 2] = j + 2;
|
|
this.indices[i + 3] = j + 0;
|
|
this.indices[i + 4] = j + 2;
|
|
this.indices[i + 5] = j + 3;
|
|
}
|
|
|
|
/**
|
|
* @property drawing
|
|
* @type Boolean
|
|
*/
|
|
this.drawing = false;
|
|
|
|
/**
|
|
* @property currentBatchSize
|
|
* @type Number
|
|
*/
|
|
this.currentBatchSize = 0;
|
|
|
|
/**
|
|
* @property currentBaseTexture
|
|
* @type BaseTexture
|
|
*/
|
|
this.currentBaseTexture = null;
|
|
|
|
/**
|
|
* @property dirty
|
|
* @type Boolean
|
|
*/
|
|
this.dirty = true;
|
|
|
|
/**
|
|
* @property textures
|
|
* @type Array
|
|
*/
|
|
this.textures = [];
|
|
|
|
/**
|
|
* @property blendModes
|
|
* @type Array
|
|
*/
|
|
this.blendModes = [];
|
|
|
|
/**
|
|
* @property shaders
|
|
* @type Array
|
|
*/
|
|
this.shaders = [];
|
|
|
|
/**
|
|
* @property sprites
|
|
* @type Array
|
|
*/
|
|
this.sprites = [];
|
|
|
|
/**
|
|
* @property defaultShader
|
|
* @type AbstractFilter
|
|
*/
|
|
this.defaultShader = null;
|
|
|
|
/**
|
|
* @property textureArray
|
|
* @type Array
|
|
*/
|
|
this.textureArray = [];
|
|
};
|
|
|
|
/**
|
|
* @method setContext
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.setContext = function (gl) {
|
|
this.MAX_TEXTURES = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;\n'
|
|
for (var index = 1; index < this.MAX_TEXTURES; ++index) {
|
|
dynamicIfs += '\telse if (vTextureIndex == ' +
|
|
index + '.0) gl_FragColor = texture2D(uSamplerArray[' +
|
|
index + '], vTextureCoord) * vColor;\n'
|
|
}
|
|
this.gl = gl;
|
|
this.defaultShader = new PIXI.AbstractFilter([
|
|
'//WebGLSpriteBatch Fragment Shader.',
|
|
'precision lowp float;',
|
|
'varying vec2 vTextureCoord;',
|
|
'varying vec4 vColor;',
|
|
'varying float vTextureIndex;',
|
|
'uniform sampler2D uSamplerArray[' + this.MAX_TEXTURES + '];',
|
|
'void main(void) {',
|
|
dynamicIfs,
|
|
'\telse gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;',
|
|
'}'
|
|
]);
|
|
|
|
// create a couple of buffers
|
|
this.vertexBuffer = gl.createBuffer();
|
|
this.indexBuffer = gl.createBuffer();
|
|
|
|
// 65535 is max index, so 65535 / 6 = 10922.
|
|
|
|
//upload the index data
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
|
|
|
|
this.currentBlendMode = 99999;
|
|
|
|
var shader = new PIXI.PixiShader(gl);
|
|
|
|
shader.fragmentSrc = this.defaultShader.fragmentSrc;
|
|
shader.uniforms = {};
|
|
shader.init();
|
|
|
|
this.defaultShader.shaders[gl.id] = shader;
|
|
};
|
|
|
|
/**
|
|
* @method begin
|
|
* @param renderSession {Object} The RenderSession object
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.begin = function (renderSession) {
|
|
this.renderSession = renderSession;
|
|
this.shader = this.renderSession.shaderManager.defaultShader;
|
|
|
|
this.start();
|
|
};
|
|
|
|
/**
|
|
* @method end
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.end = function () {
|
|
this.flush();
|
|
};
|
|
|
|
/**
|
|
* @method render
|
|
* @param sprite {Sprite} the sprite to render when using this spritebatch
|
|
* @param {Matrix} [matrix] - Optional matrix. If provided the Display Object will be rendered using this matrix, otherwise it will use its worldTransform.
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.render = function (sprite, matrix) {
|
|
var texture = sprite.texture;
|
|
var baseTexture = texture.baseTexture;
|
|
var gl = this.gl;
|
|
if (this.textureArray[baseTexture.textureIndex] != baseTexture) {
|
|
this.flush();
|
|
gl.activeTexture(gl.TEXTURE0 + baseTexture.textureIndex);
|
|
gl.bindTexture(gl.TEXTURE_2D, baseTexture._glTextures[gl.id]);
|
|
this.textureArray[baseTexture.textureIndex] = baseTexture;
|
|
}
|
|
|
|
// They provided an alternative rendering matrix, so use it
|
|
var wt = sprite.worldTransform;
|
|
|
|
if (matrix) {
|
|
wt = matrix;
|
|
}
|
|
|
|
// check texture..
|
|
if (this.currentBatchSize >= this.size) {
|
|
this.flush();
|
|
this.currentBaseTexture = texture.baseTexture;
|
|
}
|
|
|
|
// get the uvs for the texture
|
|
var uvs = texture._uvs;
|
|
|
|
// if the uvs have not updated then no point rendering just yet!
|
|
if (!uvs) {
|
|
return;
|
|
}
|
|
|
|
var aX = sprite.anchor.x;
|
|
var aY = sprite.anchor.y;
|
|
|
|
var w0, w1, h0, h1;
|
|
|
|
if (texture.trim) {
|
|
// if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.
|
|
var trim = texture.trim;
|
|
|
|
w1 = trim.x - aX * trim.width;
|
|
w0 = w1 + texture.crop.width;
|
|
|
|
h1 = trim.y - aY * trim.height;
|
|
h0 = h1 + texture.crop.height;
|
|
} else {
|
|
w0 = (texture.frame.width) * (1 - aX);
|
|
w1 = (texture.frame.width) * -aX;
|
|
|
|
h0 = texture.frame.height * (1 - aY);
|
|
h1 = texture.frame.height * -aY;
|
|
}
|
|
|
|
var i = this.currentBatchSize * this.vertexSize; //4 * this.vertSize;
|
|
var tiOffset = this.currentBatchSize * 4;
|
|
var resolution = texture.baseTexture.resolution;
|
|
var textureIndex = texture.baseTexture.textureIndex;
|
|
|
|
var a = wt.a / resolution;
|
|
var b = wt.b / resolution;
|
|
var c = wt.c / resolution;
|
|
var d = wt.d / resolution;
|
|
var tx = wt.tx;
|
|
var ty = wt.ty;
|
|
|
|
var cw = texture.crop.width;
|
|
var ch = texture.crop.height;
|
|
|
|
if (texture.rotated)
|
|
{
|
|
var a0 = wt.a;
|
|
var b0 = wt.b;
|
|
var c0 = wt.c;
|
|
var d0 = wt.d;
|
|
var _w1 = w1;
|
|
var _w0 = w0;
|
|
|
|
// Offset before rotating
|
|
tx = wt.c * ch + tx;
|
|
ty = wt.d * ch + ty;
|
|
|
|
// Rotate matrix by 90 degrees
|
|
// We use precalculated values for sine and cosine of rad(90)
|
|
a = a0 * 6.123233995736766e-17 + -c0;
|
|
b = b0 * 6.123233995736766e-17 + -d0;
|
|
c = a0 + c0 * 6.123233995736766e-17;
|
|
d = b0 + d0 * 6.123233995736766e-17;
|
|
|
|
// Update UV coordinates
|
|
texture._updateUvsInverted();
|
|
|
|
// Rotate dimensions
|
|
w0 = h0;
|
|
w1 = h1;
|
|
h0 = _w0;
|
|
h1 = _w1;
|
|
}
|
|
|
|
var colors = this.colors;
|
|
var positions = this.positions;
|
|
var tint = sprite.tint;
|
|
var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24);
|
|
|
|
if (this.renderSession.roundPixels) {
|
|
positions[i++] = a * w1 + c * h1 + tx | 0;
|
|
positions[i++] = d * h1 + b * w1 + ty | 0;
|
|
positions[i++] = uvs.x0;
|
|
positions[i++] = uvs.y0;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
|
|
positions[i++] = a * w0 + c * h1 + tx | 0;
|
|
positions[i++] = d * h1 + b * w0 + ty | 0;
|
|
positions[i++] = uvs.x1;
|
|
positions[i++] = uvs.y1;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
|
|
positions[i++] = a * w0 + c * h0 + tx | 0;
|
|
positions[i++] = d * h0 + b * w0 + ty | 0;
|
|
positions[i++] = uvs.x2;
|
|
positions[i++] = uvs.y2;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
|
|
positions[i++] = a * w1 + c * h0 + tx | 0;
|
|
positions[i++] = d * h0 + b * w1 + ty | 0;
|
|
positions[i++] = uvs.x3;
|
|
positions[i++] = uvs.y3;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
} else {
|
|
positions[i++] = a * w1 + c * h1 + tx;
|
|
positions[i++] = d * h1 + b * w1 + ty;
|
|
positions[i++] = uvs.x0;
|
|
positions[i++] = uvs.y0;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
|
|
positions[i++] = a * w0 + c * h1 + tx;
|
|
positions[i++] = d * h1 + b * w0 + ty;
|
|
positions[i++] = uvs.x1;
|
|
positions[i++] = uvs.y1;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
|
|
positions[i++] = a * w0 + c * h0 + tx;
|
|
positions[i++] = d * h0 + b * w0 + ty;
|
|
positions[i++] = uvs.x2;
|
|
positions[i++] = uvs.y2;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
|
|
positions[i++] = a * w1 + c * h0 + tx;
|
|
positions[i++] = d * h0 + b * w1 + ty;
|
|
positions[i++] = uvs.x3;
|
|
positions[i++] = uvs.y3;
|
|
colors[i++] = color;
|
|
positions[i++] = textureIndex;
|
|
}
|
|
// increment the batchsize
|
|
this.sprites[this.currentBatchSize++] = sprite;
|
|
};
|
|
|
|
/**
|
|
* Renders a TilingSprite using the spriteBatch.
|
|
*
|
|
* @method renderTilingSprite
|
|
* @param sprite {TilingSprite} the sprite to render
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function (sprite) {
|
|
var texture = sprite.tilingTexture;
|
|
var baseTexture = texture.baseTexture;
|
|
var gl = this.gl;
|
|
var textureIndex = sprite.texture.baseTexture.textureIndex;
|
|
if (this.textureArray[textureIndex] != baseTexture) {
|
|
this.flush();
|
|
gl.activeTexture(gl.TEXTURE0 + textureIndex);
|
|
gl.bindTexture(gl.TEXTURE_2D, baseTexture._glTextures[gl.id]);
|
|
this.textureArray[textureIndex] = baseTexture;
|
|
}
|
|
|
|
// check texture..
|
|
if (this.currentBatchSize >= this.size) {
|
|
this.flush();
|
|
this.currentBaseTexture = texture.baseTexture;
|
|
}
|
|
|
|
// set the textures uvs temporarily
|
|
if (!sprite._uvs) {
|
|
sprite._uvs = new PIXI.TextureUvs();
|
|
}
|
|
|
|
var uvs = sprite._uvs;
|
|
|
|
var w = texture.baseTexture.width;
|
|
var h = texture.baseTexture.height;
|
|
|
|
// var w = sprite._frame.sourceSizeW;
|
|
// var h = sprite._frame.sourceSizeH;
|
|
|
|
// w = 16;
|
|
// h = 16;
|
|
|
|
sprite.tilePosition.x %= w * sprite.tileScaleOffset.x;
|
|
sprite.tilePosition.y %= h * sprite.tileScaleOffset.y;
|
|
|
|
var offsetX = sprite.tilePosition.x / (w * sprite.tileScaleOffset.x);
|
|
var offsetY = sprite.tilePosition.y / (h * sprite.tileScaleOffset.y);
|
|
|
|
var scaleX = (sprite.width / w) / (sprite.tileScale.x * sprite.tileScaleOffset.x);
|
|
var scaleY = (sprite.height / h) / (sprite.tileScale.y * sprite.tileScaleOffset.y);
|
|
|
|
uvs.x0 = 0 - offsetX;
|
|
uvs.y0 = 0 - offsetY;
|
|
|
|
uvs.x1 = (1 * scaleX) - offsetX;
|
|
uvs.y1 = 0 - offsetY;
|
|
|
|
uvs.x2 = (1 * scaleX) - offsetX;
|
|
uvs.y2 = (1 * scaleY) - offsetY;
|
|
|
|
uvs.x3 = 0 - offsetX;
|
|
uvs.y3 = (1 * scaleY) - offsetY;
|
|
|
|
// Get the sprites current alpha and tint and combine them into a single color
|
|
var tint = sprite.tint;
|
|
var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24);
|
|
|
|
var positions = this.positions;
|
|
var colors = this.colors;
|
|
|
|
var width = sprite.width;
|
|
var height = sprite.height;
|
|
|
|
// TODO trim??
|
|
var aX = sprite.anchor.x;
|
|
var aY = sprite.anchor.y;
|
|
var w0 = width * (1 - aX);
|
|
var w1 = width * -aX;
|
|
|
|
var h0 = height * (1 - aY);
|
|
var h1 = height * -aY;
|
|
|
|
var i = this.currentBatchSize * this.vertexSize; //4 * this.vertSize;
|
|
|
|
var resolution = texture.baseTexture.resolution;
|
|
|
|
var wt = sprite.worldTransform;
|
|
|
|
var a = wt.a / resolution;
|
|
var b = wt.b / resolution;
|
|
var c = wt.c / resolution;
|
|
var d = wt.d / resolution;
|
|
var tx = wt.tx;
|
|
var ty = wt.ty;
|
|
// xy
|
|
positions[i++] = a * w1 + c * h1 + tx;
|
|
positions[i++] = d * h1 + b * w1 + ty;
|
|
// uv
|
|
positions[i++] = uvs.x0;
|
|
positions[i++] = uvs.y0;
|
|
// color
|
|
colors[i++] = color;
|
|
// texture index
|
|
positions[i++] = textureIndex;
|
|
|
|
// xy
|
|
positions[i++] = (a * w0 + c * h1 + tx);
|
|
positions[i++] = d * h1 + b * w0 + ty;
|
|
// uv
|
|
positions[i++] = uvs.x1;
|
|
positions[i++] = uvs.y1;
|
|
// color
|
|
colors[i++] = color;
|
|
// texture index
|
|
positions[i++] = textureIndex;
|
|
|
|
// xy
|
|
positions[i++] = a * w0 + c * h0 + tx;
|
|
positions[i++] = d * h0 + b * w0 + ty;
|
|
// uv
|
|
positions[i++] = uvs.x2;
|
|
positions[i++] = uvs.y2;
|
|
// color
|
|
colors[i++] = color;
|
|
// texture index
|
|
positions[i++] = textureIndex;
|
|
|
|
// xy
|
|
positions[i++] = a * w1 + c * h0 + tx;
|
|
positions[i++] = d * h0 + b * w1 + ty;
|
|
// uv
|
|
positions[i++] = uvs.x3;
|
|
positions[i++] = uvs.y3;
|
|
// color
|
|
colors[i++] = color;
|
|
// texture index
|
|
positions[i++] = textureIndex;
|
|
|
|
// increment the batchsize
|
|
this.sprites[this.currentBatchSize++] = sprite;
|
|
};
|
|
|
|
/**
|
|
* Renders the content and empties the current batch.
|
|
*
|
|
* @method flush
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.flush = function () {
|
|
// If the batch is length 0 then return as there is nothing to draw
|
|
if (this.currentBatchSize === 0) {
|
|
return;
|
|
}
|
|
|
|
var gl = this.gl;
|
|
var shader;
|
|
|
|
if (this.dirty) {
|
|
this.dirty = false;
|
|
|
|
shader = this.defaultShader.shaders[gl.id];
|
|
|
|
// bind the main texture
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
|
|
// bind the buffers
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
// this is the same for each shader?
|
|
var stride = this.vertexSize; //this.vertSize * 4;
|
|
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0);
|
|
gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, stride, 8);
|
|
|
|
// color attributes will be interpreted as unsigned bytes and normalized
|
|
gl.vertexAttribPointer(shader.colorAttribute, 4, gl.UNSIGNED_BYTE, true, stride, 16);
|
|
|
|
// Texture index
|
|
gl.vertexAttribPointer(shader.aTextureIndex, 1, gl.FLOAT, false, stride, 20);
|
|
}
|
|
|
|
// upload the verts to the buffer
|
|
if (this.currentBatchSize > (this.size * 0.5)) {
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
|
|
} else {
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
var view = this.positions.subarray(0, this.currentBatchSize * this.vertexSize);
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
|
|
}
|
|
|
|
var nextTexture, nextBlendMode, nextShader;
|
|
var batchSize = 0;
|
|
var start = 0;
|
|
|
|
var currentBaseTexture = null;
|
|
var currentBlendMode = this.renderSession.blendModeManager.currentBlendMode;
|
|
var currentShader = null;
|
|
|
|
var blendSwap = false;
|
|
var shaderSwap = false;
|
|
var sprite;
|
|
var textureIndex = 0;
|
|
|
|
for (var i = 0, j = this.currentBatchSize; i < j; i++) {
|
|
|
|
sprite = this.sprites[i];
|
|
|
|
if (sprite.tilingTexture) {
|
|
nextTexture = sprite.tilingTexture.baseTexture;
|
|
} else {
|
|
nextTexture = sprite.texture.baseTexture;
|
|
}
|
|
|
|
nextBlendMode = sprite.blendMode;
|
|
nextShader = sprite.shader || this.defaultShader;
|
|
|
|
blendSwap = currentBlendMode !== nextBlendMode;
|
|
shaderSwap = currentShader !== nextShader; // should I use _UIDS???
|
|
|
|
var skip = nextTexture.skipRender;
|
|
|
|
if (skip && sprite.children.length > 0) {
|
|
skip = false;
|
|
}
|
|
//
|
|
if (/*(currentBaseTexture != nextTexture && !skip) ||*/
|
|
blendSwap ||
|
|
shaderSwap) {
|
|
this.renderBatch(currentBaseTexture, batchSize, start);
|
|
|
|
start = i;
|
|
batchSize = 0;
|
|
currentBaseTexture = nextTexture;
|
|
|
|
if (blendSwap) {
|
|
currentBlendMode = nextBlendMode;
|
|
this.renderSession.blendModeManager.setBlendMode(currentBlendMode);
|
|
}
|
|
|
|
if (shaderSwap) {
|
|
currentShader = nextShader;
|
|
|
|
shader = currentShader.shaders[gl.id];
|
|
|
|
if (!shader) {
|
|
shader = new PIXI.PixiShader(gl);
|
|
|
|
shader.fragmentSrc = currentShader.fragmentSrc;
|
|
shader.uniforms = currentShader.uniforms;
|
|
shader.init();
|
|
|
|
currentShader.shaders[gl.id] = shader;
|
|
}
|
|
|
|
// set shader function???
|
|
this.renderSession.shaderManager.setShader(shader);
|
|
|
|
if (shader.dirty) {
|
|
shader.syncUniforms();
|
|
}
|
|
|
|
// both these only need to be set if they are changing..
|
|
// set the projection
|
|
var projection = this.renderSession.projection;
|
|
gl.uniform2f(shader.projectionVector, projection.x, projection.y);
|
|
|
|
// TODO - this is temporary!
|
|
var offsetVector = this.renderSession.offset;
|
|
gl.uniform2f(shader.offsetVector, offsetVector.x, offsetVector.y);
|
|
|
|
// set the pointers
|
|
}
|
|
}
|
|
|
|
batchSize++;
|
|
}
|
|
|
|
this.renderBatch(currentBaseTexture, batchSize, start);
|
|
|
|
// then reset the batch!
|
|
this.currentBatchSize = 0;
|
|
};
|
|
|
|
/**
|
|
* @method renderBatch
|
|
* @param texture {Texture}
|
|
* @param size {Number}
|
|
* @param startIndex {Number}
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.renderBatch = function (texture, size, startIndex) {
|
|
if (size === 0) {
|
|
return;
|
|
}
|
|
|
|
var gl = this.gl;
|
|
|
|
// check if a texture is dirty..
|
|
if (texture._dirty[gl.id]) {
|
|
if (!this.renderSession.renderer.updateTexture(texture)) {
|
|
// If updateTexture returns false then we cannot render it, so bail out now
|
|
return;
|
|
}
|
|
}
|
|
gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2);
|
|
// increment the draw count
|
|
this.renderSession.drawCount++;
|
|
};
|
|
|
|
/**
|
|
* @method stop
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.stop = function () {
|
|
this.flush();
|
|
this.dirty = true;
|
|
};
|
|
|
|
/**
|
|
* @method start
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.start = function () {
|
|
this.dirty = true;
|
|
};
|
|
|
|
/**
|
|
* Destroys the SpriteBatch.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.WebGLSpriteBatch.prototype.destroy = function () {
|
|
this.vertices = null;
|
|
this.indices = null;
|
|
|
|
this.gl.deleteBuffer(this.vertexBuffer);
|
|
this.gl.deleteBuffer(this.indexBuffer);
|
|
|
|
this.currentBaseTexture = null;
|
|
|
|
this.gl = null;
|
|
};
|
|
/**
|
|
* @author Mat Groves
|
|
*
|
|
* Big thanks to the very clever Matt DesLauriers <mattdesl> https://github.com/mattdesl/
|
|
* for creating the original pixi version!
|
|
*
|
|
* Heavily inspired by LibGDX's WebGLSpriteBatch:
|
|
* https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java
|
|
*/
|
|
|
|
/**
|
|
* @class WebGLFastSpriteBatch
|
|
* @constructor
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch = function(gl)
|
|
{
|
|
|
|
/**
|
|
* @property vertSize
|
|
* @type Number
|
|
*/
|
|
this.vertSize = 11;
|
|
|
|
/**
|
|
* @property maxSize
|
|
* @type Number
|
|
*/
|
|
this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize;
|
|
|
|
/**
|
|
* @property size
|
|
* @type Number
|
|
*/
|
|
this.size = this.maxSize;
|
|
|
|
//the total number of floats in our batch
|
|
var numVerts = this.size * 4 * this.vertSize;
|
|
|
|
//the total number of indices in our batch
|
|
var numIndices = this.maxSize * 6;
|
|
|
|
/**
|
|
* Vertex data
|
|
* @property vertices
|
|
* @type Float32Array
|
|
*/
|
|
this.vertices = new PIXI.Float32Array(numVerts);
|
|
|
|
/**
|
|
* Index data
|
|
* @property indices
|
|
* @type Uint16Array
|
|
*/
|
|
this.indices = new PIXI.Uint16Array(numIndices);
|
|
|
|
/**
|
|
* @property vertexBuffer
|
|
* @type Object
|
|
*/
|
|
this.vertexBuffer = null;
|
|
|
|
/**
|
|
* @property indexBuffer
|
|
* @type Object
|
|
*/
|
|
this.indexBuffer = null;
|
|
|
|
/**
|
|
* @property lastIndexCount
|
|
* @type Number
|
|
*/
|
|
this.lastIndexCount = 0;
|
|
|
|
for (var i=0, j=0; i < numIndices; i += 6, j += 4)
|
|
{
|
|
this.indices[i + 0] = j + 0;
|
|
this.indices[i + 1] = j + 1;
|
|
this.indices[i + 2] = j + 2;
|
|
this.indices[i + 3] = j + 0;
|
|
this.indices[i + 4] = j + 2;
|
|
this.indices[i + 5] = j + 3;
|
|
}
|
|
|
|
/**
|
|
* @property drawing
|
|
* @type Boolean
|
|
*/
|
|
this.drawing = false;
|
|
|
|
/**
|
|
* @property currentBatchSize
|
|
* @type Number
|
|
*/
|
|
this.currentBatchSize = 0;
|
|
|
|
/**
|
|
* @property currentBaseTexture
|
|
* @type BaseTexture
|
|
*/
|
|
this.currentBaseTexture = null;
|
|
|
|
/**
|
|
* @property currentBlendMode
|
|
* @type Number
|
|
*/
|
|
this.currentBlendMode = 0;
|
|
|
|
/**
|
|
* @property renderSession
|
|
* @type Object
|
|
*/
|
|
this.renderSession = null;
|
|
|
|
/**
|
|
* @property shader
|
|
* @type Object
|
|
*/
|
|
this.shader = null;
|
|
|
|
/**
|
|
* @property matrix
|
|
* @type Matrix
|
|
*/
|
|
this.matrix = null;
|
|
|
|
this.setContext(gl);
|
|
};
|
|
|
|
PIXI.WebGLFastSpriteBatch.prototype.constructor = PIXI.WebGLFastSpriteBatch;
|
|
|
|
/**
|
|
* Sets the WebGL Context.
|
|
*
|
|
* @method setContext
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl)
|
|
{
|
|
this.gl = gl;
|
|
|
|
// create a couple of buffers
|
|
this.vertexBuffer = gl.createBuffer();
|
|
this.indexBuffer = gl.createBuffer();
|
|
|
|
// 65535 is max index, so 65535 / 6 = 10922.
|
|
|
|
//upload the index data
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
|
|
};
|
|
|
|
/**
|
|
* @method begin
|
|
* @param spriteBatch {WebGLSpriteBatch}
|
|
* @param renderSession {Object}
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession)
|
|
{
|
|
this.renderSession = renderSession;
|
|
this.shader = this.renderSession.shaderManager.fastShader;
|
|
|
|
this.matrix = spriteBatch.worldTransform.toArray(true);
|
|
|
|
this.start();
|
|
};
|
|
|
|
/**
|
|
* @method end
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.end = function()
|
|
{
|
|
this.flush();
|
|
};
|
|
|
|
/**
|
|
* @method render
|
|
* @param spriteBatch {WebGLSpriteBatch}
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.render = function (spriteBatch)
|
|
{
|
|
var children = spriteBatch.children;
|
|
var sprite = children[0];
|
|
|
|
// if the uvs have not updated then no point rendering just yet!
|
|
|
|
// check texture.
|
|
if(!sprite.texture._uvs)return;
|
|
|
|
this.currentBaseTexture = sprite.texture.baseTexture;
|
|
|
|
// check blend mode
|
|
if(sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode)
|
|
{
|
|
this.flush();
|
|
this.renderSession.blendModeManager.setBlendMode(sprite.blendMode);
|
|
}
|
|
|
|
for(var i=0,j= children.length; i<j; i++)
|
|
{
|
|
this.renderSprite(children[i]);
|
|
}
|
|
|
|
this.flush();
|
|
};
|
|
|
|
/**
|
|
* @method renderSprite
|
|
* @param sprite {Sprite}
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.renderSprite = function(sprite)
|
|
{
|
|
var texture = sprite.texture;
|
|
var baseTexture = texture.baseTexture;
|
|
var gl = this.gl;
|
|
|
|
if (this.textureArray[baseTexture.textureIndex] != baseTexture) {
|
|
gl.activeTexture(gl.TEXTURE0 + baseTexture.textureIndex);
|
|
gl.bindTexture(gl.TEXTURE_2D, baseTexture._glTextures[gl.id]);
|
|
this.textureArray[baseTexture.textureIndex] = baseTexture;
|
|
this.flush();
|
|
}
|
|
//sprite = children[i];
|
|
if(!sprite.visible)return;
|
|
|
|
// TODO trim??
|
|
if(sprite.texture.baseTexture !== this.currentBaseTexture && !sprite.texture.baseTexture.skipRender)
|
|
{
|
|
this.flush();
|
|
this.currentBaseTexture = sprite.texture.baseTexture;
|
|
|
|
if(!sprite.texture._uvs)return;
|
|
}
|
|
|
|
var uvs, vertices = this.vertices, width, height, w0, w1, h0, h1, index;
|
|
|
|
uvs = sprite.texture._uvs;
|
|
|
|
width = sprite.texture.frame.width;
|
|
height = sprite.texture.frame.height;
|
|
|
|
if (sprite.texture.trim)
|
|
{
|
|
// if the sprite is trimmed then we need to add the extra space before transforming the sprite coords..
|
|
var trim = sprite.texture.trim;
|
|
|
|
w1 = trim.x - sprite.anchor.x * trim.width;
|
|
w0 = w1 + sprite.texture.crop.width;
|
|
|
|
h1 = trim.y - sprite.anchor.y * trim.height;
|
|
h0 = h1 + sprite.texture.crop.height;
|
|
}
|
|
else
|
|
{
|
|
w0 = (sprite.texture.frame.width ) * (1-sprite.anchor.x);
|
|
w1 = (sprite.texture.frame.width ) * -sprite.anchor.x;
|
|
|
|
h0 = sprite.texture.frame.height * (1-sprite.anchor.y);
|
|
h1 = sprite.texture.frame.height * -sprite.anchor.y;
|
|
}
|
|
|
|
index = this.currentBatchSize * 4 * this.vertSize;
|
|
var textureIndex = sprite.texture.baseTexture.textureIndex;
|
|
// xy
|
|
vertices[index++] = w1;
|
|
vertices[index++] = h1;
|
|
|
|
vertices[index++] = sprite.position.x;
|
|
vertices[index++] = sprite.position.y;
|
|
|
|
//scale
|
|
vertices[index++] = sprite.scale.x;
|
|
vertices[index++] = sprite.scale.y;
|
|
|
|
//rotation
|
|
vertices[index++] = sprite.rotation;
|
|
|
|
// uv
|
|
vertices[index++] = uvs.x0;
|
|
vertices[index++] = uvs.y1;
|
|
// color
|
|
vertices[index++] = sprite.alpha;
|
|
// texture Index
|
|
vertices[index++] = textureIndex;
|
|
|
|
|
|
// xy
|
|
vertices[index++] = w0;
|
|
vertices[index++] = h1;
|
|
|
|
vertices[index++] = sprite.position.x;
|
|
vertices[index++] = sprite.position.y;
|
|
|
|
//scale
|
|
vertices[index++] = sprite.scale.x;
|
|
vertices[index++] = sprite.scale.y;
|
|
|
|
//rotation
|
|
vertices[index++] = sprite.rotation;
|
|
|
|
// uv
|
|
vertices[index++] = uvs.x1;
|
|
vertices[index++] = uvs.y1;
|
|
// color
|
|
vertices[index++] = sprite.alpha;
|
|
// texture Index
|
|
vertices[index++] = textureIndex;
|
|
|
|
// xy
|
|
vertices[index++] = w0;
|
|
vertices[index++] = h0;
|
|
|
|
vertices[index++] = sprite.position.x;
|
|
vertices[index++] = sprite.position.y;
|
|
|
|
//scale
|
|
vertices[index++] = sprite.scale.x;
|
|
vertices[index++] = sprite.scale.y;
|
|
|
|
//rotation
|
|
vertices[index++] = sprite.rotation;
|
|
|
|
// uv
|
|
vertices[index++] = uvs.x2;
|
|
vertices[index++] = uvs.y2;
|
|
// color
|
|
vertices[index++] = sprite.alpha;
|
|
// texture Index
|
|
vertices[index++] = textureIndex;
|
|
|
|
|
|
|
|
// xy
|
|
vertices[index++] = w1;
|
|
vertices[index++] = h0;
|
|
|
|
vertices[index++] = sprite.position.x;
|
|
vertices[index++] = sprite.position.y;
|
|
|
|
//scale
|
|
vertices[index++] = sprite.scale.x;
|
|
vertices[index++] = sprite.scale.y;
|
|
|
|
//rotation
|
|
vertices[index++] = sprite.rotation;
|
|
|
|
// uv
|
|
vertices[index++] = uvs.x3;
|
|
vertices[index++] = uvs.y3;
|
|
// color
|
|
vertices[index++] = sprite.alpha;
|
|
// texture Index
|
|
vertices[index++] = textureIndex;
|
|
|
|
// increment the batchs
|
|
this.currentBatchSize++;
|
|
|
|
if(this.currentBatchSize >= this.size)
|
|
{
|
|
this.flush();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method flush
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.flush = function()
|
|
{
|
|
// If the batch is length 0 then return as there is nothing to draw
|
|
if (this.currentBatchSize===0)return;
|
|
|
|
var gl = this.gl;
|
|
|
|
// bind the current texture
|
|
|
|
if(!this.currentBaseTexture._glTextures[gl.id])this.renderSession.renderer.updateTexture(this.currentBaseTexture, gl);
|
|
|
|
//gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id]);
|
|
|
|
// upload the verts to the buffer
|
|
|
|
if(this.currentBatchSize > ( this.size * 0.5 ) )
|
|
{
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
|
|
}
|
|
else
|
|
{
|
|
var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize);
|
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
|
|
}
|
|
|
|
// now draw those suckas!
|
|
gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0);
|
|
|
|
// then reset the batch!
|
|
this.currentBatchSize = 0;
|
|
|
|
// increment the draw count
|
|
this.renderSession.drawCount++;
|
|
};
|
|
|
|
|
|
/**
|
|
* @method stop
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.stop = function()
|
|
{
|
|
this.flush();
|
|
};
|
|
|
|
/**
|
|
* @method start
|
|
*/
|
|
PIXI.WebGLFastSpriteBatch.prototype.start = function()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
// bind the main texture
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
|
|
// bind the buffers
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
|
|
// set the projection
|
|
var projection = this.renderSession.projection;
|
|
gl.uniform2f(this.shader.projectionVector, projection.x, projection.y);
|
|
|
|
// set the matrix
|
|
gl.uniformMatrix3fv(this.shader.uMatrix, false, this.matrix);
|
|
|
|
// set the pointers
|
|
var stride = this.vertSize * 4;
|
|
|
|
gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0);
|
|
gl.vertexAttribPointer(this.shader.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4);
|
|
gl.vertexAttribPointer(this.shader.aScale, 2, gl.FLOAT, false, stride, 4 * 4);
|
|
gl.vertexAttribPointer(this.shader.aRotation, 1, gl.FLOAT, false, stride, 6 * 4);
|
|
gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4);
|
|
gl.vertexAttribPointer(this.shader.colorAttribute, 1, gl.FLOAT, false, stride, 9 * 4);
|
|
gl.vertexAttribPointer(this.shader.aTextureIndex, 1, gl.FLOAT, false, stride, 10 * 4);
|
|
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* @class WebGLFilterManager
|
|
* @constructor
|
|
*/
|
|
PIXI.WebGLFilterManager = function()
|
|
{
|
|
/**
|
|
* @property filterStack
|
|
* @type Array
|
|
*/
|
|
this.filterStack = [];
|
|
|
|
/**
|
|
* @property offsetX
|
|
* @type Number
|
|
*/
|
|
this.offsetX = 0;
|
|
|
|
/**
|
|
* @property offsetY
|
|
* @type Number
|
|
*/
|
|
this.offsetY = 0;
|
|
};
|
|
|
|
PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager;
|
|
|
|
/**
|
|
* Initialises the context and the properties.
|
|
*
|
|
* @method setContext
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
*/
|
|
PIXI.WebGLFilterManager.prototype.setContext = function(gl)
|
|
{
|
|
this.gl = gl;
|
|
this.texturePool = [];
|
|
|
|
this.initShaderBuffers();
|
|
};
|
|
|
|
/**
|
|
* @method begin
|
|
* @param renderSession {RenderSession}
|
|
* @param buffer {ArrayBuffer}
|
|
*/
|
|
PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer)
|
|
{
|
|
this.renderSession = renderSession;
|
|
this.defaultShader = renderSession.shaderManager.defaultShader;
|
|
|
|
var projection = this.renderSession.projection;
|
|
this.width = projection.x * 2;
|
|
this.height = -projection.y * 2;
|
|
this.buffer = buffer;
|
|
};
|
|
|
|
/**
|
|
* Applies the filter and adds it to the current filter stack.
|
|
*
|
|
* @method pushFilter
|
|
* @param filterBlock {Object} the filter that will be pushed to the current filter stack
|
|
*/
|
|
PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock)
|
|
{
|
|
var gl = this.gl;
|
|
|
|
var projection = this.renderSession.projection;
|
|
var offset = this.renderSession.offset;
|
|
|
|
filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds();
|
|
|
|
// >>> modify by nextht
|
|
filterBlock._previous_stencil_mgr = this.renderSession.stencilManager;
|
|
this.renderSession.stencilManager = new PIXI.WebGLStencilManager();
|
|
this.renderSession.stencilManager.setContext(gl);
|
|
gl.disable(gl.STENCIL_TEST);
|
|
// <<< modify by nextht
|
|
|
|
// filter program
|
|
// OPTIMISATION - the first filter is free if its a simple color change?
|
|
this.filterStack.push(filterBlock);
|
|
|
|
var filter = filterBlock.filterPasses[0];
|
|
|
|
this.offsetX += filterBlock._filterArea.x;
|
|
this.offsetY += filterBlock._filterArea.y;
|
|
|
|
var texture = this.texturePool.pop();
|
|
if(!texture)
|
|
{
|
|
texture = new PIXI.FilterTexture(this.gl, this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
|
|
}
|
|
else
|
|
{
|
|
texture.resize(this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
|
|
}
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
|
|
|
|
var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea;
|
|
|
|
var padding = filter.padding;
|
|
filterArea.x -= padding;
|
|
filterArea.y -= padding;
|
|
filterArea.width += padding * 2;
|
|
filterArea.height += padding * 2;
|
|
|
|
// cap filter to screen size..
|
|
if(filterArea.x < 0)filterArea.x = 0;
|
|
if(filterArea.width > this.width)filterArea.width = this.width;
|
|
if(filterArea.y < 0)filterArea.y = 0;
|
|
if(filterArea.height > this.height)filterArea.height = this.height;
|
|
|
|
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);
|
|
|
|
// set view port
|
|
gl.viewport(0, 0, filterArea.width * this.renderSession.resolution, filterArea.height * this.renderSession.resolution);
|
|
|
|
projection.x = filterArea.width/2;
|
|
projection.y = -filterArea.height/2;
|
|
|
|
offset.x = -filterArea.x;
|
|
offset.y = -filterArea.y;
|
|
|
|
// update projection
|
|
// now restore the regular shader..
|
|
// this.renderSession.shaderManager.setShader(this.defaultShader);
|
|
//gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2);
|
|
//gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y);
|
|
|
|
gl.colorMask(true, true, true, true);
|
|
gl.clearColor(0,0,0, 0);
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
|
|
filterBlock._glFilterTexture = texture;
|
|
|
|
};
|
|
|
|
/**
|
|
* Removes the last filter from the filter stack and doesn't return it.
|
|
*
|
|
* @method popFilter
|
|
*/
|
|
PIXI.WebGLFilterManager.prototype.popFilter = function()
|
|
{
|
|
var gl = this.gl;
|
|
var filterBlock = this.filterStack.pop();
|
|
var filterArea = filterBlock._filterArea;
|
|
var texture = filterBlock._glFilterTexture;
|
|
var projection = this.renderSession.projection;
|
|
var offset = this.renderSession.offset;
|
|
|
|
if(filterBlock.filterPasses.length > 1)
|
|
{
|
|
gl.viewport(0, 0, filterArea.width * this.renderSession.resolution, filterArea.height * this.renderSession.resolution);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
|
|
this.vertexArray[0] = 0;
|
|
this.vertexArray[1] = filterArea.height;
|
|
|
|
this.vertexArray[2] = filterArea.width;
|
|
this.vertexArray[3] = filterArea.height;
|
|
|
|
this.vertexArray[4] = 0;
|
|
this.vertexArray[5] = 0;
|
|
|
|
this.vertexArray[6] = filterArea.width;
|
|
this.vertexArray[7] = 0;
|
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
|
|
// now set the uvs..
|
|
this.uvArray[2] = filterArea.width/this.width;
|
|
this.uvArray[5] = filterArea.height/this.height;
|
|
this.uvArray[6] = filterArea.width/this.width;
|
|
this.uvArray[7] = filterArea.height/this.height;
|
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);
|
|
|
|
var inputTexture = texture;
|
|
var outputTexture = this.texturePool.pop();
|
|
if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
|
|
outputTexture.resize(this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
|
|
|
|
// need to clear this FBO as it may have some left over elements from a previous filter.
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
|
|
gl.disable(gl.BLEND);
|
|
|
|
for (var i = 0; i < filterBlock.filterPasses.length-1; i++)
|
|
{
|
|
var filterPass = filterBlock.filterPasses[i];
|
|
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
|
|
|
|
// set texture
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);
|
|
|
|
// draw texture..
|
|
//filterPass.applyFilterPass(filterArea.width, filterArea.height);
|
|
this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height);
|
|
|
|
// swap the textures..
|
|
var temp = inputTexture;
|
|
inputTexture = outputTexture;
|
|
outputTexture = temp;
|
|
}
|
|
|
|
gl.enable(gl.BLEND);
|
|
|
|
texture = inputTexture;
|
|
this.texturePool.push(outputTexture);
|
|
}
|
|
|
|
var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1];
|
|
|
|
this.offsetX -= filterArea.x;
|
|
this.offsetY -= filterArea.y;
|
|
|
|
var sizeX = this.width;
|
|
var sizeY = this.height;
|
|
|
|
var offsetX = 0;
|
|
var offsetY = 0;
|
|
|
|
var buffer = this.buffer;
|
|
|
|
// time to render the filters texture to the previous scene
|
|
if(this.filterStack.length === 0)
|
|
{
|
|
gl.colorMask(true, true, true, true);//this.transparent);
|
|
}
|
|
else
|
|
{
|
|
var currentFilter = this.filterStack[this.filterStack.length-1];
|
|
filterArea = currentFilter._filterArea;
|
|
|
|
sizeX = filterArea.width;
|
|
sizeY = filterArea.height;
|
|
|
|
offsetX = filterArea.x;
|
|
offsetY = filterArea.y;
|
|
|
|
buffer = currentFilter._glFilterTexture.frameBuffer;
|
|
}
|
|
|
|
// TODO need to remove these global elements..
|
|
projection.x = sizeX/2;
|
|
projection.y = -sizeY/2;
|
|
|
|
offset.x = offsetX;
|
|
offset.y = offsetY;
|
|
|
|
filterArea = filterBlock._filterArea;
|
|
|
|
var x = filterArea.x-offsetX;
|
|
var y = filterArea.y-offsetY;
|
|
|
|
// update the buffers..
|
|
// make sure to flip the y!
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
|
|
this.vertexArray[0] = x;
|
|
this.vertexArray[1] = y + filterArea.height;
|
|
|
|
this.vertexArray[2] = x + filterArea.width;
|
|
this.vertexArray[3] = y + filterArea.height;
|
|
|
|
this.vertexArray[4] = x;
|
|
this.vertexArray[5] = y;
|
|
|
|
this.vertexArray[6] = x + filterArea.width;
|
|
this.vertexArray[7] = y;
|
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
|
|
|
|
this.uvArray[2] = filterArea.width/this.width;
|
|
this.uvArray[5] = filterArea.height/this.height;
|
|
this.uvArray[6] = filterArea.width/this.width;
|
|
this.uvArray[7] = filterArea.height/this.height;
|
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);
|
|
|
|
gl.viewport(0, 0, sizeX * this.renderSession.resolution, sizeY * this.renderSession.resolution);
|
|
|
|
// bind the buffer
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, buffer );
|
|
|
|
// set the blend mode!
|
|
//gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
|
|
|
|
// set texture
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
|
|
|
|
// >>> modify by nextht
|
|
if (this.renderSession.stencilManager) {
|
|
this.renderSession.stencilManager.destroy();
|
|
}
|
|
this.renderSession.stencilManager = filterBlock._previous_stencil_mgr;
|
|
filterBlock._previous_stencil_mgr = null;
|
|
if (this.renderSession.stencilManager.count > 0) {
|
|
gl.enable(gl.STENCIL_TEST);
|
|
}
|
|
else {
|
|
gl.disable(gl.STENCIL_TEST);
|
|
}
|
|
// <<< modify by nextht
|
|
|
|
// apply!
|
|
this.applyFilterPass(filter, filterArea, sizeX, sizeY);
|
|
|
|
// now restore the regular shader.. should happen automatically now..
|
|
// this.renderSession.shaderManager.setShader(this.defaultShader);
|
|
// gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2);
|
|
// gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY);
|
|
|
|
// return the texture to the pool
|
|
this.texturePool.push(texture);
|
|
filterBlock._glFilterTexture = null;
|
|
};
|
|
|
|
|
|
/**
|
|
* Applies the filter to the specified area.
|
|
*
|
|
* @method applyFilterPass
|
|
* @param filter {AbstractFilter} the filter that needs to be applied
|
|
* @param filterArea {Texture} TODO - might need an update
|
|
* @param width {Number} the horizontal range of the filter
|
|
* @param height {Number} the vertical range of the filter
|
|
*/
|
|
PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height)
|
|
{
|
|
// use program
|
|
var gl = this.gl;
|
|
var shader = filter.shaders[gl.id];
|
|
|
|
if(!shader)
|
|
{
|
|
shader = new PIXI.PixiShader(gl);
|
|
|
|
shader.fragmentSrc = filter.fragmentSrc;
|
|
shader.uniforms = filter.uniforms;
|
|
shader.init(true);
|
|
|
|
filter.shaders[gl.id] = shader;
|
|
}
|
|
|
|
// set the shader
|
|
this.renderSession.shaderManager.setShader(shader);
|
|
|
|
// gl.useProgram(shader.program);
|
|
|
|
gl.uniform2f(shader.projectionVector, width/2, -height/2);
|
|
gl.uniform2f(shader.offsetVector, 0,0);
|
|
|
|
if(filter.uniforms.dimensions)
|
|
{
|
|
filter.uniforms.dimensions.value[0] = this.width;//width;
|
|
filter.uniforms.dimensions.value[1] = this.height;//height;
|
|
filter.uniforms.dimensions.value[2] = this.vertexArray[0];
|
|
filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height;
|
|
}
|
|
|
|
shader.syncUniforms();
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
|
|
gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
|
|
gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
|
|
// draw the filter...
|
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
this.renderSession.drawCount++;
|
|
};
|
|
|
|
/**
|
|
* Initialises the shader buffers.
|
|
*
|
|
* @method initShaderBuffers
|
|
*/
|
|
PIXI.WebGLFilterManager.prototype.initShaderBuffers = function()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
// create some buffers
|
|
this.vertexBuffer = gl.createBuffer();
|
|
this.uvBuffer = gl.createBuffer();
|
|
this.colorBuffer = gl.createBuffer();
|
|
this.indexBuffer = gl.createBuffer();
|
|
|
|
// bind and upload the vertexs..
|
|
// keep a reference to the vertexFloatData..
|
|
this.vertexArray = new PIXI.Float32Array([0.0, 0.0,
|
|
1.0, 0.0,
|
|
0.0, 1.0,
|
|
1.0, 1.0]);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW);
|
|
|
|
// bind and upload the uv buffer
|
|
this.uvArray = new PIXI.Float32Array([0.0, 0.0,
|
|
1.0, 0.0,
|
|
0.0, 1.0,
|
|
1.0, 1.0]);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW);
|
|
|
|
this.colorArray = new PIXI.Float32Array([1.0, 0xFFFFFF,
|
|
1.0, 0xFFFFFF,
|
|
1.0, 0xFFFFFF,
|
|
1.0, 0xFFFFFF]);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW);
|
|
|
|
// bind and upload the index
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW);
|
|
|
|
};
|
|
|
|
/**
|
|
* Destroys the filter and removes it from the filter stack.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.WebGLFilterManager.prototype.destroy = function()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
this.filterStack = null;
|
|
|
|
this.offsetX = 0;
|
|
this.offsetY = 0;
|
|
|
|
// destroy textures
|
|
for (var i = 0; i < this.texturePool.length; i++) {
|
|
this.texturePool[i].destroy();
|
|
}
|
|
|
|
this.texturePool = null;
|
|
|
|
//destroy buffers..
|
|
gl.deleteBuffer(this.vertexBuffer);
|
|
gl.deleteBuffer(this.uvBuffer);
|
|
gl.deleteBuffer(this.colorBuffer);
|
|
gl.deleteBuffer(this.indexBuffer);
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
function _CreateEmptyTexture(gl, width, height, scaleMode) {
|
|
var texture = gl.createTexture();
|
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
return texture;
|
|
}
|
|
|
|
var _fbErrors = {
|
|
36054: 'Incomplete attachment',
|
|
36055: 'Missing attachment',
|
|
36057: 'Incomplete dimensions',
|
|
36061: 'Framebuffer unsupported'
|
|
};
|
|
|
|
function _CreateFramebuffer(gl, width, height, scaleMode, textureUnit) {
|
|
var framebuffer = gl.createFramebuffer();
|
|
var depthStencilBuffer = gl.createRenderbuffer();
|
|
var colorBuffer = null;
|
|
var fbStatus = 0;
|
|
|
|
gl.activeTexture(gl.TEXTURE0 + textureUnit);
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
|
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer);
|
|
colorBuffer = _CreateEmptyTexture(gl, width, height, scaleMode);
|
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorBuffer, 0);
|
|
fbStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
|
if(fbStatus !== gl.FRAMEBUFFER_COMPLETE) {
|
|
console.error('Incomplete GL framebuffer. ', _fbErrors[fbStatus]);
|
|
}
|
|
framebuffer.width = width;
|
|
framebuffer.height = height;
|
|
framebuffer.targetTexture = colorBuffer;
|
|
framebuffer.renderBuffer = depthStencilBuffer;
|
|
return framebuffer;
|
|
}
|
|
|
|
/**
|
|
* @class FilterTexture
|
|
* @constructor
|
|
* @param gl {WebGLContext} the current WebGL drawing context
|
|
* @param width {Number} the horizontal range of the filter
|
|
* @param height {Number} the vertical range of the filter
|
|
* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values
|
|
*/
|
|
PIXI.FilterTexture = function(gl, width, height, scaleMode, textureUnit)
|
|
{
|
|
textureUnit = typeof textureUnit === 'number' ? textureUnit : 0;
|
|
/**
|
|
* @property gl
|
|
* @type WebGLContext
|
|
*/
|
|
this.gl = gl;
|
|
// next time to create a frame buffer and texture
|
|
|
|
/**
|
|
* @property frameBuffer
|
|
* @type Any
|
|
*/
|
|
this.frameBuffer = _CreateFramebuffer(gl, width, height, scaleMode || PIXI.scaleModes.DEFAULT, textureUnit);
|
|
/**
|
|
* @property texture
|
|
* @type Any
|
|
*/
|
|
this.texture = this.frameBuffer.targetTexture;
|
|
this.width = width;
|
|
this.height = height;
|
|
this.renderBuffer = this.frameBuffer.renderBuffer;
|
|
};
|
|
|
|
PIXI.FilterTexture.prototype.constructor = PIXI.FilterTexture;
|
|
|
|
/**
|
|
* Clears the filter texture.
|
|
*
|
|
* @method clear
|
|
*/
|
|
PIXI.FilterTexture.prototype.clear = function()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
gl.clearColor(0,0,0, 0);
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
};
|
|
|
|
/**
|
|
* Resizes the texture to the specified width and height
|
|
*
|
|
* @method resize
|
|
* @param width {Number} the new width of the texture
|
|
* @param height {Number} the new height of the texture
|
|
*/
|
|
PIXI.FilterTexture.prototype.resize = function(width, height)
|
|
{
|
|
if(this.width === width && this.height === height) return;
|
|
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
var gl = this.gl;
|
|
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
// update the stencil buffer width and height
|
|
gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer);
|
|
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height );
|
|
};
|
|
|
|
/**
|
|
* Destroys the filter texture.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.FilterTexture.prototype.destroy = function()
|
|
{
|
|
var gl = this.gl;
|
|
gl.deleteFramebuffer( this.frameBuffer );
|
|
gl.deleteTexture( this.texture );
|
|
|
|
this.frameBuffer = null;
|
|
this.texture = null;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* Creates a Canvas element of the given size.
|
|
*
|
|
* @class CanvasBuffer
|
|
* @constructor
|
|
* @param width {Number} the width for the newly created canvas
|
|
* @param height {Number} the height for the newly created canvas
|
|
*/
|
|
PIXI.CanvasBuffer = function(width, height)
|
|
{
|
|
/**
|
|
* The width of the Canvas in pixels.
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
this.width = width;
|
|
|
|
/**
|
|
* The height of the Canvas in pixels.
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
this.height = height;
|
|
|
|
/**
|
|
* The Canvas object that belongs to this CanvasBuffer.
|
|
*
|
|
* @property canvas
|
|
* @type HTMLCanvasElement
|
|
*/
|
|
this.canvas = PIXI.CanvasPool.create(this, this.width, this.height);
|
|
|
|
/**
|
|
* A CanvasRenderingContext2D object representing a two-dimensional rendering context.
|
|
*
|
|
* @property context
|
|
* @type CanvasRenderingContext2D
|
|
*/
|
|
this.context = this.canvas.getContext("2d");
|
|
|
|
this.canvas.width = width;
|
|
this.canvas.height = height;
|
|
};
|
|
|
|
PIXI.CanvasBuffer.prototype.constructor = PIXI.CanvasBuffer;
|
|
|
|
/**
|
|
* Clears the canvas that was created by the CanvasBuffer class.
|
|
*
|
|
* @method clear
|
|
* @private
|
|
*/
|
|
PIXI.CanvasBuffer.prototype.clear = function()
|
|
{
|
|
this.context.setTransform(1, 0, 0, 1, 0, 0);
|
|
this.context.clearRect(0,0, this.width, this.height);
|
|
};
|
|
|
|
/**
|
|
* Resizes the canvas to the specified width and height.
|
|
*
|
|
* @method resize
|
|
* @param width {Number} the new width of the canvas
|
|
* @param height {Number} the new height of the canvas
|
|
*/
|
|
PIXI.CanvasBuffer.prototype.resize = function(width, height)
|
|
{
|
|
this.width = this.canvas.width = width;
|
|
this.height = this.canvas.height = height;
|
|
};
|
|
|
|
/**
|
|
* Frees the canvas up for use again.
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.CanvasBuffer.prototype.destroy = function()
|
|
{
|
|
PIXI.CanvasPool.remove(this);
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* A set of functions used to handle masking.
|
|
*
|
|
* @class CanvasMaskManager
|
|
* @constructor
|
|
*/
|
|
PIXI.CanvasMaskManager = function()
|
|
{
|
|
};
|
|
|
|
PIXI.CanvasMaskManager.prototype.constructor = PIXI.CanvasMaskManager;
|
|
|
|
/**
|
|
* This method adds it to the current stack of masks.
|
|
*
|
|
* @method pushMask
|
|
* @param maskData {Object} the maskData that will be pushed
|
|
* @param renderSession {Object} The renderSession whose context will be used for this mask manager.
|
|
*/
|
|
PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, renderSession) {
|
|
|
|
var context = renderSession.context;
|
|
|
|
context.save();
|
|
|
|
var cacheAlpha = maskData.alpha;
|
|
var transform = maskData.worldTransform;
|
|
|
|
var resolution = renderSession.resolution;
|
|
|
|
context.setTransform(transform.a * resolution,
|
|
transform.b * resolution,
|
|
transform.c * resolution,
|
|
transform.d * resolution,
|
|
transform.tx * resolution,
|
|
transform.ty * resolution);
|
|
|
|
PIXI.CanvasGraphics.renderGraphicsMask(maskData, context);
|
|
|
|
context.clip();
|
|
|
|
maskData.worldAlpha = cacheAlpha;
|
|
};
|
|
|
|
/**
|
|
* Restores the current drawing context to the state it was before the mask was applied.
|
|
*
|
|
* @method popMask
|
|
* @param renderSession {Object} The renderSession whose context will be used for this mask manager.
|
|
*/
|
|
PIXI.CanvasMaskManager.prototype.popMask = function(renderSession)
|
|
{
|
|
renderSession.context.restore();
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* Utility methods for Sprite/Texture tinting.
|
|
*
|
|
* @class CanvasTinter
|
|
* @static
|
|
*/
|
|
PIXI.CanvasTinter = function() {};
|
|
|
|
/**
|
|
* Basically this method just needs a sprite and a color and tints the sprite with the given color.
|
|
*
|
|
* @method getTintedTexture
|
|
* @static
|
|
* @param sprite {Sprite} the sprite to tint
|
|
* @param color {Number} the color to use to tint the sprite with
|
|
* @return {HTMLCanvasElement} The tinted canvas
|
|
*/
|
|
PIXI.CanvasTinter.getTintedTexture = function(sprite, color)
|
|
{
|
|
var canvas = sprite.tintedTexture || PIXI.CanvasPool.create(this);
|
|
|
|
PIXI.CanvasTinter.tintMethod(sprite.texture, color, canvas);
|
|
|
|
return canvas;
|
|
};
|
|
|
|
/**
|
|
* Tint a texture using the "multiply" operation.
|
|
*
|
|
* @method tintWithMultiply
|
|
* @static
|
|
* @param texture {Texture} the texture to tint
|
|
* @param color {Number} the color to use to tint the sprite with
|
|
* @param canvas {HTMLCanvasElement} the current canvas
|
|
*/
|
|
PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas)
|
|
{
|
|
var context = canvas.getContext("2d");
|
|
|
|
var crop = texture.crop;
|
|
var w = crop.width;
|
|
var h = crop.height;
|
|
|
|
if (texture.rotated)
|
|
{
|
|
w = h;
|
|
h = crop.width;
|
|
}
|
|
|
|
if (canvas.width !== w || canvas.height !== h)
|
|
{
|
|
canvas.width = w;
|
|
canvas.height = h;
|
|
}
|
|
|
|
context.clearRect(0, 0, w, h);
|
|
|
|
context.fillStyle = "#" + ("00000" + (color | 0).toString(16)).substr(-6);
|
|
context.fillRect(0, 0, w, h);
|
|
|
|
context.globalCompositeOperation = "multiply";
|
|
context.drawImage(texture.baseTexture.source, crop.x, crop.y, w, h, 0, 0, w, h);
|
|
|
|
context.globalCompositeOperation = "destination-atop";
|
|
context.drawImage(texture.baseTexture.source, crop.x, crop.y, w, h, 0, 0, w, h);
|
|
|
|
};
|
|
|
|
/**
|
|
* Tint a texture pixel per pixel.
|
|
*
|
|
* @method tintPerPixel
|
|
* @static
|
|
* @param texture {Texture} the texture to tint
|
|
* @param color {Number} the color to use to tint the sprite with
|
|
* @param canvas {HTMLCanvasElement} the current canvas
|
|
*/
|
|
PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas)
|
|
{
|
|
var context = canvas.getContext("2d");
|
|
|
|
var crop = texture.crop;
|
|
var w = crop.width;
|
|
var h = crop.height;
|
|
|
|
if (texture.rotated)
|
|
{
|
|
w = h;
|
|
h = crop.width;
|
|
}
|
|
|
|
if (canvas.width !== w || canvas.height !== h)
|
|
{
|
|
canvas.width = w;
|
|
canvas.height = h;
|
|
}
|
|
|
|
context.globalCompositeOperation = "copy";
|
|
|
|
context.drawImage(texture.baseTexture.source, crop.x, crop.y, w, h, 0, 0, w, h);
|
|
|
|
var rgbValues = PIXI.hex2rgb(color);
|
|
var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2];
|
|
|
|
var pixelData = context.getImageData(0, 0, w, h);
|
|
|
|
var pixels = pixelData.data;
|
|
|
|
for (var i = 0; i < pixels.length; i += 4)
|
|
{
|
|
pixels[i + 0] *= r;
|
|
pixels[i + 1] *= g;
|
|
pixels[i + 2] *= b;
|
|
|
|
if (!PIXI.CanvasTinter.canHandleAlpha)
|
|
{
|
|
var alpha = pixels[i + 3];
|
|
|
|
pixels[i + 0] /= 255 / alpha;
|
|
pixels[i + 1] /= 255 / alpha;
|
|
pixels[i + 2] /= 255 / alpha;
|
|
}
|
|
}
|
|
|
|
context.putImageData(pixelData, 0, 0);
|
|
};
|
|
|
|
/**
|
|
* Checks if the browser correctly supports putImageData alpha channels.
|
|
*
|
|
* @method checkInverseAlpha
|
|
* @static
|
|
*/
|
|
PIXI.CanvasTinter.checkInverseAlpha = function()
|
|
{
|
|
var canvas = new PIXI.CanvasBuffer(2, 1);
|
|
|
|
canvas.context.fillStyle = "rgba(10, 20, 30, 0.5)";
|
|
|
|
// Draw a single pixel
|
|
canvas.context.fillRect(0, 0, 1, 1);
|
|
|
|
// Get the color values
|
|
var s1 = canvas.context.getImageData(0, 0, 1, 1);
|
|
|
|
if (s1 === null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Plot them to x2
|
|
canvas.context.putImageData(s1, 1, 0);
|
|
|
|
// Get those values
|
|
var s2 = canvas.context.getImageData(1, 0, 1, 1);
|
|
|
|
// Compare and return
|
|
return (s2.data[0] === s1.data[0] && s2.data[1] === s1.data[1] && s2.data[2] === s1.data[2] && s2.data[3] === s1.data[3]);
|
|
};
|
|
|
|
/**
|
|
* If the browser isn't capable of handling tinting with alpha this will be false.
|
|
* This property is only applicable if using tintWithPerPixel.
|
|
*
|
|
* @property canHandleAlpha
|
|
* @type Boolean
|
|
* @static
|
|
*/
|
|
PIXI.CanvasTinter.canHandleAlpha = PIXI.CanvasTinter.checkInverseAlpha();
|
|
|
|
/**
|
|
* Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method.
|
|
*
|
|
* @property canUseMultiply
|
|
* @type Boolean
|
|
* @static
|
|
*/
|
|
PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes();
|
|
|
|
/**
|
|
* The tinting method that will be used.
|
|
*
|
|
* @method tintMethod
|
|
* @static
|
|
*/
|
|
PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel;
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
|
|
* Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :)
|
|
*
|
|
* @class CanvasRenderer
|
|
* @constructor
|
|
* @param game {Phaser.Game} A reference to the Phaser Game instance
|
|
*/
|
|
PIXI.CanvasRenderer = function (game) {
|
|
|
|
/**
|
|
* @property {Phaser.Game} game - A reference to the Phaser Game instance.
|
|
*/
|
|
this.game = game;
|
|
|
|
if (!PIXI.defaultRenderer)
|
|
{
|
|
PIXI.defaultRenderer = this;
|
|
}
|
|
|
|
/**
|
|
* The renderer type.
|
|
*
|
|
* @property type
|
|
* @type Number
|
|
*/
|
|
this.type = PIXI.CANVAS_RENDERER;
|
|
|
|
/**
|
|
* The resolution of the canvas.
|
|
*
|
|
* @property resolution
|
|
* @type Number
|
|
*/
|
|
this.resolution = game.resolution;
|
|
|
|
/**
|
|
* This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
|
|
* If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color.
|
|
* If the Stage is transparent Pixi will use clearRect to clear the canvas every frame.
|
|
* Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set.
|
|
*
|
|
* @property clearBeforeRender
|
|
* @type Boolean
|
|
* @default
|
|
*/
|
|
this.clearBeforeRender = game.clearBeforeRender;
|
|
|
|
/**
|
|
* Whether the render view is transparent
|
|
*
|
|
* @property transparent
|
|
* @type Boolean
|
|
*/
|
|
this.transparent = game.transparent;
|
|
|
|
/**
|
|
* Whether the render view should be resized automatically
|
|
*
|
|
* @property autoResize
|
|
* @type Boolean
|
|
*/
|
|
this.autoResize = false;
|
|
|
|
/**
|
|
* The width of the canvas view
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
* @default 800
|
|
*/
|
|
this.width = game.width * this.resolution;
|
|
|
|
/**
|
|
* The height of the canvas view
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
* @default 600
|
|
*/
|
|
this.height = game.height * this.resolution;
|
|
|
|
/**
|
|
* The canvas element that everything is drawn to.
|
|
*
|
|
* @property view
|
|
* @type HTMLCanvasElement
|
|
*/
|
|
this.view = game.canvas;
|
|
|
|
/**
|
|
* The canvas 2d context that everything is drawn with
|
|
* @property context
|
|
* @type CanvasRenderingContext2D
|
|
*/
|
|
this.context = this.view.getContext("2d", { alpha: this.transparent } );
|
|
|
|
/**
|
|
* Boolean flag controlling canvas refresh.
|
|
*
|
|
* @property refresh
|
|
* @type Boolean
|
|
*/
|
|
this.refresh = true;
|
|
|
|
/**
|
|
* Internal var.
|
|
*
|
|
* @property count
|
|
* @type Number
|
|
*/
|
|
this.count = 0;
|
|
|
|
/**
|
|
* Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer
|
|
* @property CanvasMaskManager
|
|
* @type CanvasMaskManager
|
|
*/
|
|
this.maskManager = new PIXI.CanvasMaskManager();
|
|
|
|
/**
|
|
* The render session is just a bunch of parameter used for rendering
|
|
* @property renderSession
|
|
* @type Object
|
|
*/
|
|
this.renderSession = {
|
|
context: this.context,
|
|
maskManager: this.maskManager,
|
|
scaleMode: null,
|
|
smoothProperty: Phaser.Canvas.getSmoothingPrefix(this.context),
|
|
|
|
/**
|
|
* If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
|
|
* Handy for crisp pixel art and speed on legacy devices.
|
|
*/
|
|
roundPixels: false
|
|
};
|
|
|
|
this.mapBlendModes();
|
|
|
|
this.resize(this.width, this.height);
|
|
|
|
};
|
|
|
|
// constructor
|
|
PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer;
|
|
|
|
/**
|
|
* Renders the DisplayObjectContainer, usually the Phaser.Stage, to this canvas view.
|
|
*
|
|
* @method render
|
|
* @param root {Phaser.Stage|PIXI.DisplayObjectContainer} The root element to be rendered.
|
|
*/
|
|
PIXI.CanvasRenderer.prototype.render = function (root) {
|
|
|
|
this.context.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
|
this.context.globalAlpha = 1;
|
|
|
|
this.renderSession.currentBlendMode = 0;
|
|
this.renderSession.shakeX = this.game.camera._shake.x;
|
|
this.renderSession.shakeY = this.game.camera._shake.y;
|
|
|
|
this.context.globalCompositeOperation = 'source-over';
|
|
|
|
if (navigator.isCocoonJS && this.view.screencanvas)
|
|
{
|
|
this.context.fillStyle = "black";
|
|
this.context.clear();
|
|
}
|
|
|
|
if (this.clearBeforeRender)
|
|
{
|
|
if (this.transparent)
|
|
{
|
|
this.context.clearRect(0, 0, this.width, this.height);
|
|
}
|
|
else if (root._bgColor)
|
|
{
|
|
this.context.fillStyle = root._bgColor.rgba;
|
|
this.context.fillRect(0, 0, this.width , this.height);
|
|
}
|
|
}
|
|
|
|
this.renderDisplayObject(root);
|
|
|
|
};
|
|
|
|
PIXI.CanvasRenderer.prototype.setTexturePriority = function (textureNameCollection) {
|
|
|
|
// Does nothing on Canvas, but here to allow you to simply set
|
|
// `game.renderer.setTexturePriority()` without having to worry about
|
|
// running in WebGL or not.
|
|
|
|
};
|
|
|
|
/**
|
|
* Removes everything from the renderer and optionally removes the Canvas DOM element.
|
|
*
|
|
* @method destroy
|
|
* @param [removeView=true] {boolean} Removes the Canvas element from the DOM.
|
|
*/
|
|
PIXI.CanvasRenderer.prototype.destroy = function (removeView) {
|
|
|
|
if (removeView === undefined) { removeView = true; }
|
|
|
|
if (removeView && this.view.parent)
|
|
{
|
|
this.view.parent.removeChild(this.view);
|
|
}
|
|
|
|
this.view = null;
|
|
this.context = null;
|
|
this.maskManager = null;
|
|
this.renderSession = null;
|
|
|
|
};
|
|
|
|
/**
|
|
* Resizes the canvas view to the specified width and height
|
|
*
|
|
* @method resize
|
|
* @param width {Number} the new width of the canvas view
|
|
* @param height {Number} the new height of the canvas view
|
|
*/
|
|
PIXI.CanvasRenderer.prototype.resize = function (width, height) {
|
|
|
|
this.width = width * this.resolution;
|
|
this.height = height * this.resolution;
|
|
|
|
this.view.width = this.width;
|
|
this.view.height = this.height;
|
|
|
|
if (this.autoResize)
|
|
{
|
|
this.view.style.width = this.width / this.resolution + "px";
|
|
this.view.style.height = this.height / this.resolution + "px";
|
|
}
|
|
|
|
if (this.renderSession.smoothProperty)
|
|
{
|
|
this.context[this.renderSession.smoothProperty] = (this.renderSession.scaleMode === PIXI.scaleModes.LINEAR);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders a display object
|
|
*
|
|
* @method renderDisplayObject
|
|
* @param displayObject {DisplayObject} The displayObject to render
|
|
* @param context {CanvasRenderingContext2D} the context 2d method of the canvas
|
|
* @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering.
|
|
* @private
|
|
*/
|
|
PIXI.CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context, matrix) {
|
|
|
|
this.renderSession.context = context || this.context;
|
|
this.renderSession.resolution = this.resolution;
|
|
displayObject._renderCanvas(this.renderSession, matrix);
|
|
|
|
};
|
|
|
|
/**
|
|
* Maps Pixi blend modes to canvas blend modes.
|
|
*
|
|
* @method mapBlendModes
|
|
* @private
|
|
*/
|
|
PIXI.CanvasRenderer.prototype.mapBlendModes = function () {
|
|
|
|
if (!PIXI.blendModesCanvas)
|
|
{
|
|
var b = [];
|
|
var modes = PIXI.blendModes;
|
|
var useNew = PIXI.canUseNewCanvasBlendModes();
|
|
|
|
b[modes.NORMAL] = 'source-over';
|
|
b[modes.ADD] = 'lighter';
|
|
b[modes.MULTIPLY] = (useNew) ? 'multiply' : 'source-over';
|
|
b[modes.SCREEN] = (useNew) ? 'screen' : 'source-over';
|
|
b[modes.OVERLAY] = (useNew) ? 'overlay' : 'source-over';
|
|
b[modes.DARKEN] = (useNew) ? 'darken' : 'source-over';
|
|
b[modes.LIGHTEN] = (useNew) ? 'lighten' : 'source-over';
|
|
b[modes.COLOR_DODGE] = (useNew) ? 'color-dodge' : 'source-over';
|
|
b[modes.COLOR_BURN] = (useNew) ? 'color-burn' : 'source-over';
|
|
b[modes.HARD_LIGHT] = (useNew) ? 'hard-light' : 'source-over';
|
|
b[modes.SOFT_LIGHT] = (useNew) ? 'soft-light' : 'source-over';
|
|
b[modes.DIFFERENCE] = (useNew) ? 'difference' : 'source-over';
|
|
b[modes.EXCLUSION] = (useNew) ? 'exclusion' : 'source-over';
|
|
b[modes.HUE] = (useNew) ? 'hue' : 'source-over';
|
|
b[modes.SATURATION] = (useNew) ? 'saturation' : 'source-over';
|
|
b[modes.COLOR] = (useNew) ? 'color' : 'source-over';
|
|
b[modes.LUMINOSITY] = (useNew) ? 'luminosity' : 'source-over';
|
|
|
|
PIXI.blendModesCanvas = b;
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* A texture stores the information that represents an image. All textures have a base texture.
|
|
*
|
|
* @class BaseTexture
|
|
* @uses EventTarget
|
|
* @constructor
|
|
* @param source {String|Canvas} the source object (image or canvas)
|
|
* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values
|
|
*/
|
|
PIXI.BaseTexture = function(source, scaleMode)
|
|
{
|
|
/**
|
|
* The Resolution of the texture.
|
|
*
|
|
* @property resolution
|
|
* @type Number
|
|
*/
|
|
this.resolution = 1;
|
|
|
|
/**
|
|
* [read-only] The width of the base texture set when the image has loaded
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
* @readOnly
|
|
*/
|
|
this.width = 100;
|
|
|
|
/**
|
|
* [read-only] The height of the base texture set when the image has loaded
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
* @readOnly
|
|
*/
|
|
this.height = 100;
|
|
|
|
/**
|
|
* The scale mode to apply when scaling this texture
|
|
*
|
|
* @property scaleMode
|
|
* @type {Number}
|
|
* @default PIXI.scaleModes.LINEAR
|
|
*/
|
|
this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT;
|
|
|
|
/**
|
|
* [read-only] Set to true once the base texture has loaded
|
|
*
|
|
* @property hasLoaded
|
|
* @type Boolean
|
|
* @readOnly
|
|
*/
|
|
this.hasLoaded = false;
|
|
|
|
/**
|
|
* The image source that is used to create the texture.
|
|
*
|
|
* @property source
|
|
* @type Image
|
|
*/
|
|
this.source = source;
|
|
|
|
/**
|
|
* Controls if RGB channels should be pre-multiplied by Alpha (WebGL only)
|
|
*
|
|
* @property premultipliedAlpha
|
|
* @type Boolean
|
|
* @default true
|
|
*/
|
|
this.premultipliedAlpha = true;
|
|
|
|
// used for webGL
|
|
|
|
/**
|
|
* @property _glTextures
|
|
* @type Array
|
|
* @private
|
|
*/
|
|
this._glTextures = [];
|
|
|
|
/**
|
|
* Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used
|
|
* Also the texture must be a power of two size to work
|
|
*
|
|
* @property mipmap
|
|
* @type {Boolean}
|
|
*/
|
|
this.mipmap = false;
|
|
|
|
/**
|
|
* The multi texture batching index number.
|
|
* @property textureIndex
|
|
* @type Number
|
|
*/
|
|
this.textureIndex = 0;
|
|
|
|
/**
|
|
* @property _dirty
|
|
* @type Array
|
|
* @private
|
|
*/
|
|
this._dirty = [true, true, true, true];
|
|
|
|
if (!source)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((this.source.complete || this.source.getContext) && this.source.width && this.source.height)
|
|
{
|
|
this.hasLoaded = true;
|
|
this.width = this.source.naturalWidth || this.source.width;
|
|
this.height = this.source.naturalHeight || this.source.height;
|
|
this.dirty();
|
|
}
|
|
|
|
/**
|
|
* A BaseTexture can be set to skip the rendering phase in the WebGL Sprite Batch.
|
|
*
|
|
* You may want to do this if you have a parent Sprite with no visible texture (i.e. uses the internal `__default` texture)
|
|
* that has children that you do want to render, without causing a batch flush in the process.
|
|
*
|
|
* @property skipRender
|
|
* @type Boolean
|
|
*/
|
|
this.skipRender = false;
|
|
|
|
/**
|
|
* @property _powerOf2
|
|
* @type Boolean
|
|
* @private
|
|
*/
|
|
this._powerOf2 = false;
|
|
|
|
};
|
|
|
|
PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture;
|
|
|
|
/**
|
|
* Forces this BaseTexture to be set as loaded, with the given width and height.
|
|
* Then calls BaseTexture.dirty.
|
|
* Important for when you don't want to modify the source object by forcing in `complete` or dimension properties it may not have.
|
|
*
|
|
* @method forceLoaded
|
|
* @param {number} width - The new width to force the BaseTexture to be.
|
|
* @param {number} height - The new height to force the BaseTexture to be.
|
|
*/
|
|
PIXI.BaseTexture.prototype.forceLoaded = function(width, height)
|
|
{
|
|
this.hasLoaded = true;
|
|
this.width = width;
|
|
this.height = height;
|
|
this.dirty();
|
|
};
|
|
|
|
/**
|
|
* Destroys this base texture
|
|
*
|
|
* @method destroy
|
|
*/
|
|
PIXI.BaseTexture.prototype.destroy = function()
|
|
{
|
|
if (this.source)
|
|
{
|
|
PIXI.CanvasPool.removeByCanvas(this.source);
|
|
}
|
|
|
|
this.source = null;
|
|
|
|
this.unloadFromGPU();
|
|
};
|
|
|
|
/**
|
|
* Changes the source image of the texture
|
|
*
|
|
* @method updateSourceImage
|
|
* @param newSrc {String} the path of the image
|
|
* @deprecated This method is deprecated. Please use Phaser.Sprite.loadTexture instead.
|
|
*/
|
|
PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc)
|
|
{
|
|
console.warn("PIXI.BaseTexture.updateSourceImage is deprecated. Use Phaser.Sprite.loadTexture instead.");
|
|
};
|
|
|
|
/**
|
|
* Sets all glTextures to be dirty.
|
|
*
|
|
* @method dirty
|
|
*/
|
|
PIXI.BaseTexture.prototype.dirty = function()
|
|
{
|
|
for (var i = 0; i < this._glTextures.length; i++)
|
|
{
|
|
this._dirty[i] = true;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Removes the base texture from the GPU, useful for managing resources on the GPU.
|
|
* Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it.
|
|
*
|
|
* @method unloadFromGPU
|
|
*/
|
|
PIXI.BaseTexture.prototype.unloadFromGPU = function()
|
|
{
|
|
this.dirty();
|
|
|
|
// delete the webGL textures if any.
|
|
for (var i = this._glTextures.length - 1; i >= 0; i--)
|
|
{
|
|
var glTexture = this._glTextures[i];
|
|
var gl = PIXI.glContexts[i];
|
|
|
|
if(gl && glTexture)
|
|
{
|
|
gl.deleteTexture(glTexture);
|
|
}
|
|
|
|
}
|
|
|
|
this._glTextures.length = 0;
|
|
|
|
this.dirty();
|
|
};
|
|
|
|
/**
|
|
* Helper function that creates a base texture from the given canvas element.
|
|
*
|
|
* @static
|
|
* @method fromCanvas
|
|
* @param canvas {Canvas} The canvas element source of the texture
|
|
* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values
|
|
* @return {BaseTexture}
|
|
*/
|
|
PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode)
|
|
{
|
|
if (canvas.width === 0)
|
|
{
|
|
canvas.width = 1;
|
|
}
|
|
|
|
if (canvas.height === 0)
|
|
{
|
|
canvas.height = 1;
|
|
}
|
|
|
|
return new PIXI.BaseTexture(canvas, scaleMode);
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* TextureSilentFail is a boolean that defaults to `false`.
|
|
* If `true` then `PIXI.Texture.setFrame` will no longer throw an error if the texture dimensions are incorrect.
|
|
* Instead `Texture.valid` will be set to `false` (#1556)
|
|
*
|
|
* @type {boolean}
|
|
*/
|
|
PIXI.TextureSilentFail = false;
|
|
|
|
/**
|
|
* A texture stores the information that represents an image or part of an image. It cannot be added
|
|
* to the display list directly. Instead use it as the texture for a PIXI.Sprite. If no frame is provided then the whole image is used.
|
|
*
|
|
* @class Texture
|
|
* @uses EventTarget
|
|
* @constructor
|
|
* @param baseTexture {BaseTexture} The base texture source to create the texture from
|
|
* @param frame {Rectangle} The rectangle frame of the texture to show
|
|
* @param [crop] {Rectangle} The area of original texture
|
|
* @param [trim] {Rectangle} Trimmed texture rectangle
|
|
*/
|
|
PIXI.Texture = function(baseTexture, frame, crop, trim)
|
|
{
|
|
/**
|
|
* Does this Texture have any frame data assigned to it?
|
|
*
|
|
* @property noFrame
|
|
* @type Boolean
|
|
*/
|
|
this.noFrame = false;
|
|
|
|
if (!frame)
|
|
{
|
|
this.noFrame = true;
|
|
frame = new PIXI.Rectangle(0,0,1,1);
|
|
}
|
|
|
|
if (baseTexture instanceof PIXI.Texture)
|
|
{
|
|
baseTexture = baseTexture.baseTexture;
|
|
}
|
|
|
|
/**
|
|
* The base texture that this texture uses.
|
|
*
|
|
* @property baseTexture
|
|
* @type BaseTexture
|
|
*/
|
|
this.baseTexture = baseTexture;
|
|
|
|
/**
|
|
* The frame specifies the region of the base texture that this texture uses
|
|
*
|
|
* @property frame
|
|
* @type Rectangle
|
|
*/
|
|
this.frame = frame;
|
|
|
|
/**
|
|
* The texture trim data.
|
|
*
|
|
* @property trim
|
|
* @type Rectangle
|
|
*/
|
|
this.trim = trim;
|
|
|
|
/**
|
|
* This will let the renderer know if the texture is valid. If it's not then it cannot be rendered.
|
|
*
|
|
* @property valid
|
|
* @type Boolean
|
|
*/
|
|
this.valid = false;
|
|
|
|
/**
|
|
* Is this a tiling texture? As used by the likes of a TilingSprite.
|
|
*
|
|
* @property isTiling
|
|
* @type Boolean
|
|
*/
|
|
this.isTiling = false;
|
|
|
|
/**
|
|
* This will let a renderer know that a texture has been updated (used mainly for webGL uv updates)
|
|
*
|
|
* @property requiresUpdate
|
|
* @type Boolean
|
|
*/
|
|
this.requiresUpdate = false;
|
|
|
|
/**
|
|
* This will let a renderer know that a tinted parent has updated its texture.
|
|
*
|
|
* @property requiresReTint
|
|
* @type Boolean
|
|
*/
|
|
this.requiresReTint = false;
|
|
|
|
/**
|
|
* The WebGL UV data cache.
|
|
*
|
|
* @property _uvs
|
|
* @type Object
|
|
* @private
|
|
*/
|
|
this._uvs = null;
|
|
|
|
/**
|
|
* The width of the Texture in pixels.
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
this.width = 0;
|
|
|
|
/**
|
|
* The height of the Texture in pixels.
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
this.height = 0;
|
|
|
|
/**
|
|
* This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering,
|
|
* irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases)
|
|
*
|
|
* @property crop
|
|
* @type Rectangle
|
|
*/
|
|
this.crop = crop || new PIXI.Rectangle(0, 0, 1, 1);
|
|
|
|
/**
|
|
* A flag that controls if this frame is rotated or not.
|
|
* Rotation allows you to use rotated frames in texture atlas packing, it has nothing to do with
|
|
* Sprite rotation.
|
|
*
|
|
* @property rotated
|
|
* @type Boolean
|
|
*/
|
|
this.rotated = false;
|
|
|
|
if (baseTexture.hasLoaded)
|
|
{
|
|
if (this.noFrame) frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height);
|
|
this.setFrame(frame);
|
|
}
|
|
|
|
};
|
|
|
|
PIXI.Texture.prototype.constructor = PIXI.Texture;
|
|
|
|
/**
|
|
* Called when the base texture is loaded
|
|
*
|
|
* @method onBaseTextureLoaded
|
|
* @private
|
|
*/
|
|
PIXI.Texture.prototype.onBaseTextureLoaded = function()
|
|
{
|
|
var baseTexture = this.baseTexture;
|
|
|
|
if (this.noFrame)
|
|
{
|
|
this.frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height);
|
|
}
|
|
|
|
this.setFrame(this.frame);
|
|
};
|
|
|
|
/**
|
|
* 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();
|
|
|
|
this.valid = false;
|
|
};
|
|
|
|
/**
|
|
* Specifies the region of the baseTexture that this texture will use.
|
|
*
|
|
* @method setFrame
|
|
* @param frame {Rectangle} The frame of the texture to set it to
|
|
*/
|
|
PIXI.Texture.prototype.setFrame = function(frame)
|
|
{
|
|
this.noFrame = false;
|
|
|
|
this.frame = frame;
|
|
this.width = frame.width;
|
|
this.height = frame.height;
|
|
|
|
this.crop.x = frame.x;
|
|
this.crop.y = frame.y;
|
|
this.crop.width = frame.width;
|
|
this.crop.height = frame.height;
|
|
|
|
if (!this.trim && (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height))
|
|
{
|
|
if (!PIXI.TextureSilentFail)
|
|
{
|
|
throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this);
|
|
}
|
|
|
|
this.valid = false;
|
|
return;
|
|
}
|
|
|
|
this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded;
|
|
|
|
if (this.trim)
|
|
{
|
|
this.width = this.trim.width;
|
|
this.height = this.trim.height;
|
|
this.frame.width = this.trim.width;
|
|
this.frame.height = this.trim.height;
|
|
}
|
|
|
|
if (this.valid) this._updateUvs();
|
|
|
|
};
|
|
|
|
/**
|
|
* Updates the internal WebGL UV cache.
|
|
*
|
|
* @method _updateUvs
|
|
* @private
|
|
*/
|
|
PIXI.Texture.prototype._updateUvs = function()
|
|
{
|
|
if(!this._uvs)this._uvs = new PIXI.TextureUvs();
|
|
|
|
var frame = this.crop;
|
|
var tw = this.baseTexture.width;
|
|
var th = this.baseTexture.height;
|
|
|
|
this._uvs.x0 = frame.x / tw;
|
|
this._uvs.y0 = frame.y / th;
|
|
|
|
this._uvs.x1 = (frame.x + frame.width) / tw;
|
|
this._uvs.y1 = frame.y / th;
|
|
|
|
this._uvs.x2 = (frame.x + frame.width) / tw;
|
|
this._uvs.y2 = (frame.y + frame.height) / th;
|
|
|
|
this._uvs.x3 = frame.x / tw;
|
|
this._uvs.y3 = (frame.y + frame.height) / th;
|
|
};
|
|
|
|
/**
|
|
* Updates the internal WebGL UV cache.
|
|
*
|
|
* @method _updateUvsInverted
|
|
* @private
|
|
*/
|
|
PIXI.Texture.prototype._updateUvsInverted = function () {
|
|
|
|
if (!this._uvs) { this._uvs = new PIXI.TextureUvs(); }
|
|
|
|
var frame = this.crop;
|
|
var tw = this.baseTexture.width;
|
|
var th = this.baseTexture.height;
|
|
|
|
this._uvs.x0 = frame.x / tw;
|
|
this._uvs.y0 = frame.y / th;
|
|
|
|
this._uvs.x1 = (frame.x + frame.height) / tw;
|
|
this._uvs.y1 = frame.y / th;
|
|
|
|
this._uvs.x2 = (frame.x + frame.height) / tw;
|
|
this._uvs.y2 = (frame.y + frame.width) / th;
|
|
|
|
this._uvs.x3 = frame.x / tw;
|
|
this._uvs.y3 = (frame.y + frame.width) / th;
|
|
|
|
};
|
|
|
|
/**
|
|
* Helper function that creates a new a Texture based on the given canvas element.
|
|
*
|
|
* @static
|
|
* @method fromCanvas
|
|
* @param canvas {Canvas} The canvas element source of the texture
|
|
* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values
|
|
* @return {Texture}
|
|
*/
|
|
PIXI.Texture.fromCanvas = function(canvas, scaleMode)
|
|
{
|
|
var baseTexture = PIXI.BaseTexture.fromCanvas(canvas, scaleMode);
|
|
|
|
return new PIXI.Texture(baseTexture);
|
|
};
|
|
|
|
PIXI.TextureUvs = function()
|
|
{
|
|
this.x0 = 0;
|
|
this.y0 = 0;
|
|
|
|
this.x1 = 0;
|
|
this.y1 = 0;
|
|
|
|
this.x2 = 0;
|
|
this.y2 = 0;
|
|
|
|
this.x3 = 0;
|
|
this.y3 = 0;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* A RenderTexture is a special texture that allows any Pixi display object to be rendered to it.
|
|
*
|
|
* __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded otherwise black rectangles will be drawn instead.
|
|
*
|
|
* A RenderTexture takes a snapshot of any Display Object given to its render method. The position and rotation of the given Display Objects is ignored. For example:
|
|
*
|
|
* var renderTexture = new PIXI.RenderTexture(800, 600);
|
|
* var sprite = PIXI.Sprite.fromImage("spinObj_01.png");
|
|
* sprite.position.x = 800/2;
|
|
* sprite.position.y = 600/2;
|
|
* sprite.anchor.x = 0.5;
|
|
* sprite.anchor.y = 0.5;
|
|
* renderTexture.render(sprite);
|
|
*
|
|
* The Sprite in this case will be rendered to a position of 0,0. To render this sprite at its actual position a DisplayObjectContainer should be used:
|
|
*
|
|
* var doc = new PIXI.DisplayObjectContainer();
|
|
* doc.addChild(sprite);
|
|
* renderTexture.render(doc); // Renders to center of renderTexture
|
|
*
|
|
* @class RenderTexture
|
|
* @extends Texture
|
|
* @constructor
|
|
* @param width {Number} The width of the render texture
|
|
* @param height {Number} The height of the render texture
|
|
* @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture
|
|
* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values
|
|
* @param resolution {Number} The resolution of the texture being generated
|
|
*/
|
|
PIXI.RenderTexture = function(width, height, renderer, scaleMode, resolution, textureUnit)
|
|
{
|
|
textureUnit = typeof textureUnit == 'number' ? textureUnit : 0;
|
|
/**
|
|
* The with of the render texture
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
this.width = width || 100;
|
|
|
|
/**
|
|
* The height of the render texture
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
this.height = height || 100;
|
|
|
|
/**
|
|
* The Resolution of the texture.
|
|
*
|
|
* @property resolution
|
|
* @type Number
|
|
*/
|
|
this.resolution = resolution || 1;
|
|
|
|
/**
|
|
* The framing rectangle of the render texture
|
|
*
|
|
* @property frame
|
|
* @type Rectangle
|
|
*/
|
|
this.frame = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution);
|
|
|
|
/**
|
|
* This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering,
|
|
* irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases)
|
|
*
|
|
* @property crop
|
|
* @type Rectangle
|
|
*/
|
|
this.crop = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution);
|
|
|
|
/**
|
|
* The base texture object that this texture uses
|
|
*
|
|
* @property baseTexture
|
|
* @type BaseTexture
|
|
*/
|
|
this.baseTexture = new PIXI.BaseTexture();
|
|
this.baseTexture.width = this.width * this.resolution;
|
|
this.baseTexture.height = this.height * this.resolution;
|
|
this.baseTexture._glTextures = [];
|
|
this.baseTexture.resolution = this.resolution;
|
|
|
|
this.baseTexture.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT;
|
|
|
|
this.baseTexture.hasLoaded = true;
|
|
|
|
PIXI.Texture.call(this,
|
|
this.baseTexture,
|
|
new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution)
|
|
);
|
|
|
|
/**
|
|
* The renderer this RenderTexture uses. A RenderTexture can only belong to one renderer at the moment if its webGL.
|
|
*
|
|
* @property renderer
|
|
* @type CanvasRenderer|WebGLRenderer
|
|
*/
|
|
this.renderer = renderer || PIXI.defaultRenderer;
|
|
|
|
if (this.renderer.type === PIXI.WEBGL_RENDERER)
|
|
{
|
|
var gl = this.renderer.gl;
|
|
this.baseTexture.textureIndex = textureUnit;
|
|
this.baseTexture._dirty[gl.id] = false;
|
|
|
|
this.textureBuffer = new PIXI.FilterTexture(gl, this.width, this.height, this.baseTexture.scaleMode, textureUnit);
|
|
this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture;
|
|
|
|
this.render = this.renderWebGL;
|
|
this.projection = new PIXI.Point(this.width * 0.5, -this.height * 0.5);
|
|
}
|
|
else
|
|
{
|
|
this.render = this.renderCanvas;
|
|
this.textureBuffer = new PIXI.CanvasBuffer(this.width * this.resolution, this.height * this.resolution);
|
|
this.baseTexture.source = this.textureBuffer.canvas;
|
|
}
|
|
|
|
/**
|
|
* @property valid
|
|
* @type Boolean
|
|
*/
|
|
this.valid = true;
|
|
|
|
this.tempMatrix = new Phaser.Matrix();
|
|
|
|
this._updateUvs();
|
|
};
|
|
|
|
PIXI.RenderTexture.prototype = Object.create(PIXI.Texture.prototype);
|
|
PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture;
|
|
|
|
/**
|
|
* Resizes the RenderTexture.
|
|
*
|
|
* @method resize
|
|
* @param width {Number} The width to resize to.
|
|
* @param height {Number} The height to resize to.
|
|
* @param updateBase {Boolean} Should the baseTexture.width and height values be resized as well?
|
|
*/
|
|
PIXI.RenderTexture.prototype.resize = function(width, height, updateBase)
|
|
{
|
|
if (width === this.width && height === this.height)return;
|
|
|
|
this.valid = (width > 0 && height > 0);
|
|
|
|
this.width = width;
|
|
this.height = height;
|
|
this.frame.width = this.crop.width = width * this.resolution;
|
|
this.frame.height = this.crop.height = height * this.resolution;
|
|
|
|
if (updateBase)
|
|
{
|
|
this.baseTexture.width = this.width * this.resolution;
|
|
this.baseTexture.height = this.height * this.resolution;
|
|
}
|
|
|
|
if (this.renderer.type === PIXI.WEBGL_RENDERER)
|
|
{
|
|
this.projection.x = this.width / 2;
|
|
this.projection.y = -this.height / 2;
|
|
}
|
|
|
|
if(!this.valid)return;
|
|
|
|
this.textureBuffer.resize(this.width, this.height);
|
|
};
|
|
|
|
/**
|
|
* Clears the RenderTexture.
|
|
*
|
|
* @method clear
|
|
*/
|
|
PIXI.RenderTexture.prototype.clear = function()
|
|
{
|
|
if (!this.valid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this.renderer.type === PIXI.WEBGL_RENDERER)
|
|
{
|
|
this.renderer.gl.bindFramebuffer(this.renderer.gl.FRAMEBUFFER, this.textureBuffer.frameBuffer);
|
|
}
|
|
|
|
this.textureBuffer.clear();
|
|
};
|
|
|
|
/**
|
|
* This function will draw the display object to the texture.
|
|
*
|
|
* @method renderWebGL
|
|
* @param displayObject {DisplayObject} The display object to render this texture on
|
|
* @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering.
|
|
* @param [clear] {Boolean} If true the texture will be cleared before the displayObject is drawn
|
|
* @private
|
|
*/
|
|
PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, matrix, clear)
|
|
{
|
|
if (!this.valid || displayObject.alpha === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Let's create a nice matrix to apply to our display object.
|
|
// Frame buffers come in upside down so we need to flip the matrix.
|
|
var wt = displayObject.worldTransform;
|
|
wt.identity();
|
|
wt.translate(0, this.projection.y * 2);
|
|
|
|
if (matrix)
|
|
{
|
|
wt.append(matrix);
|
|
}
|
|
|
|
wt.scale(1, -1);
|
|
|
|
// Time to update all the children of the displayObject with the new matrix.
|
|
for (var i = 0; i < displayObject.children.length; i++)
|
|
{
|
|
displayObject.children[i].updateTransform();
|
|
}
|
|
|
|
// Time for the webGL fun stuff!
|
|
var gl = this.renderer.gl;
|
|
|
|
gl.viewport(0, 0, this.width * this.resolution, this.height * this.resolution);
|
|
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer );
|
|
|
|
if (clear)
|
|
{
|
|
this.textureBuffer.clear();
|
|
}
|
|
|
|
this.renderer.spriteBatch.dirty = true;
|
|
|
|
this.renderer.renderDisplayObject(displayObject, this.projection, this.textureBuffer.frameBuffer, matrix);
|
|
|
|
this.renderer.spriteBatch.dirty = true;
|
|
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
};
|
|
|
|
/**
|
|
* This function will draw the display object to the texture.
|
|
*
|
|
* @method renderCanvas
|
|
* @param displayObject {DisplayObject} The display object to render this texture on
|
|
* @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering.
|
|
* @param [clear] {Boolean} If true the texture will be cleared before the displayObject is drawn
|
|
* @private
|
|
*/
|
|
PIXI.RenderTexture.prototype.renderCanvas = function(displayObject, matrix, clear)
|
|
{
|
|
if (!this.valid || displayObject.alpha === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Let's create a nice matrix to apply to our display object.
|
|
// Frame buffers come in upside down so we need to flip the matrix.
|
|
var wt = displayObject.worldTransform;
|
|
wt.identity();
|
|
|
|
if (matrix)
|
|
{
|
|
wt.append(matrix);
|
|
}
|
|
|
|
// Time to update all the children of the displayObject with the new matrix (what new matrix? there isn't one!)
|
|
for (var i = 0; i < displayObject.children.length; i++)
|
|
{
|
|
displayObject.children[i].updateTransform();
|
|
}
|
|
|
|
if (clear)
|
|
{
|
|
this.textureBuffer.clear();
|
|
}
|
|
|
|
var realResolution = this.renderer.resolution;
|
|
|
|
this.renderer.resolution = this.resolution;
|
|
|
|
this.renderer.renderDisplayObject(displayObject, this.textureBuffer.context, matrix);
|
|
|
|
this.renderer.resolution = realResolution;
|
|
};
|
|
|
|
/**
|
|
* Will return a HTML Image of the texture
|
|
*
|
|
* @method getImage
|
|
* @return {Image}
|
|
*/
|
|
PIXI.RenderTexture.prototype.getImage = function()
|
|
{
|
|
var image = new Image();
|
|
image.src = this.getBase64();
|
|
return image;
|
|
};
|
|
|
|
/**
|
|
* Will return a base64 encoded string of this texture. It works by calling RenderTexture.getCanvas and then running toDataURL on that.
|
|
*
|
|
* @method getBase64
|
|
* @return {String} A base64 encoded string of the texture.
|
|
*/
|
|
PIXI.RenderTexture.prototype.getBase64 = function()
|
|
{
|
|
return this.getCanvas().toDataURL();
|
|
};
|
|
|
|
/**
|
|
* Creates a Canvas element, renders this RenderTexture to it and then returns it.
|
|
*
|
|
* @method getCanvas
|
|
* @return {HTMLCanvasElement} A Canvas element with the texture rendered on.
|
|
*/
|
|
PIXI.RenderTexture.prototype.getCanvas = function()
|
|
{
|
|
if (this.renderer.type === PIXI.WEBGL_RENDERER)
|
|
{
|
|
var gl = this.renderer.gl;
|
|
var width = this.textureBuffer.width;
|
|
var height = this.textureBuffer.height;
|
|
|
|
var webGLPixels = new Uint8Array(4 * width * height);
|
|
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer);
|
|
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, webGLPixels);
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
|
|
var tempCanvas = new PIXI.CanvasBuffer(width, height);
|
|
var canvasData = tempCanvas.context.getImageData(0, 0, width, height);
|
|
canvasData.data.set(webGLPixels);
|
|
|
|
tempCanvas.context.putImageData(canvasData, 0, 0);
|
|
|
|
return tempCanvas.canvas;
|
|
}
|
|
else
|
|
{
|
|
return this.textureBuffer.canvas;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
/**
|
|
* This is the base class for creating a PIXI filter. Currently only webGL supports filters.
|
|
* If you want to make a custom filter this should be your base class.
|
|
*
|
|
* @class AbstractFilter
|
|
* @constructor
|
|
* @param fragmentSrc {Array} The fragment source in an array of strings.
|
|
* @param uniforms {Object} An object containing the uniforms for this filter.
|
|
*/
|
|
PIXI.AbstractFilter = function(fragmentSrc, uniforms)
|
|
{
|
|
/**
|
|
* An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion.
|
|
* For example the blur filter has two passes blurX and blurY.
|
|
* @property passes
|
|
* @type Array
|
|
* @private
|
|
*/
|
|
this.passes = [this];
|
|
|
|
/**
|
|
* @property shaders
|
|
* @type Array
|
|
* @private
|
|
*/
|
|
this.shaders = [];
|
|
|
|
/**
|
|
* @property dirty
|
|
* @type Boolean
|
|
*/
|
|
this.dirty = true;
|
|
|
|
/**
|
|
* @property padding
|
|
* @type Number
|
|
*/
|
|
this.padding = 0;
|
|
|
|
/**
|
|
* @property uniforms
|
|
* @type Object
|
|
* @private
|
|
*/
|
|
this.uniforms = uniforms || {};
|
|
|
|
/**
|
|
* @property fragmentSrc
|
|
* @type Array
|
|
* @private
|
|
*/
|
|
this.fragmentSrc = fragmentSrc || [];
|
|
};
|
|
|
|
PIXI.AbstractFilter.prototype.constructor = PIXI.AbstractFilter;
|
|
|
|
/**
|
|
* Syncs the uniforms between the class object and the shaders.
|
|
*
|
|
* @method syncUniforms
|
|
*/
|
|
PIXI.AbstractFilter.prototype.syncUniforms = function()
|
|
{
|
|
for(var i=0,j=this.shaders.length; i<j; i++)
|
|
{
|
|
this.shaders[i].dirty = true;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @class Strip
|
|
* @extends DisplayObjectContainer
|
|
* @constructor
|
|
* @param texture {Texture} The texture to use
|
|
* @param width {Number} the width
|
|
* @param height {Number} the height
|
|
*
|
|
*/
|
|
PIXI.Strip = function(texture)
|
|
{
|
|
PIXI.DisplayObjectContainer.call( this );
|
|
|
|
|
|
/**
|
|
* The texture of the strip
|
|
*
|
|
* @property texture
|
|
* @type Texture
|
|
*/
|
|
this.texture = texture;
|
|
|
|
// set up the main bits..
|
|
this.uvs = new PIXI.Float32Array([0, 1,
|
|
1, 1,
|
|
1, 0,
|
|
0, 1]);
|
|
|
|
this.vertices = new PIXI.Float32Array([0, 0,
|
|
100, 0,
|
|
100, 100,
|
|
0, 100]);
|
|
|
|
this.colors = new PIXI.Float32Array([1, 1, 1, 1]);
|
|
|
|
this.indices = new PIXI.Uint16Array([0, 1, 2, 3]);
|
|
|
|
/**
|
|
* Whether the strip is dirty or not
|
|
*
|
|
* @property dirty
|
|
* @type Boolean
|
|
*/
|
|
this.dirty = true;
|
|
|
|
/**
|
|
* The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode.
|
|
*
|
|
* @property blendMode
|
|
* @type Number
|
|
* @default PIXI.blendModes.NORMAL;
|
|
*/
|
|
this.blendMode = PIXI.blendModes.NORMAL;
|
|
|
|
/**
|
|
* Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other.
|
|
*
|
|
* @property canvasPadding
|
|
* @type Number
|
|
*/
|
|
this.canvasPadding = 0;
|
|
|
|
this.drawMode = PIXI.Strip.DrawModes.TRIANGLE_STRIP;
|
|
|
|
};
|
|
|
|
// constructor
|
|
PIXI.Strip.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
|
|
PIXI.Strip.prototype.constructor = PIXI.Strip;
|
|
|
|
PIXI.Strip.prototype._renderWebGL = function(renderSession)
|
|
{
|
|
// if the sprite is not visible or the alpha is 0 then no need to render this element
|
|
if(!this.visible || this.alpha <= 0)return;
|
|
// render triangle strip..
|
|
|
|
renderSession.spriteBatch.stop();
|
|
|
|
// init! init!
|
|
if(!this._vertexBuffer)this._initWebGL(renderSession);
|
|
|
|
renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader);
|
|
|
|
this._renderStrip(renderSession);
|
|
|
|
///renderSession.shaderManager.activateDefaultShader();
|
|
|
|
renderSession.spriteBatch.start();
|
|
|
|
//TODO check culling
|
|
};
|
|
|
|
PIXI.Strip.prototype._initWebGL = function(renderSession)
|
|
{
|
|
// build the strip!
|
|
var gl = renderSession.gl;
|
|
|
|
this._vertexBuffer = gl.createBuffer();
|
|
this._indexBuffer = gl.createBuffer();
|
|
this._uvBuffer = gl.createBuffer();
|
|
this._colorBuffer = gl.createBuffer();
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW);
|
|
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
|
|
};
|
|
|
|
PIXI.Strip.prototype._renderStrip = function(renderSession)
|
|
{
|
|
var gl = renderSession.gl;
|
|
var projection = renderSession.projection,
|
|
offset = renderSession.offset,
|
|
shader = renderSession.shaderManager.stripShader;
|
|
|
|
var drawMode = this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP ? gl.TRIANGLE_STRIP : gl.TRIANGLES;
|
|
|
|
// gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real);
|
|
|
|
renderSession.blendModeManager.setBlendMode(this.blendMode);
|
|
|
|
|
|
// set uniforms
|
|
gl.uniformMatrix3fv(shader.translationMatrix, false, this.worldTransform.toArray(true));
|
|
gl.uniform2f(shader.projectionVector, projection.x, -projection.y);
|
|
gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);
|
|
gl.uniform1f(shader.alpha, this.worldAlpha);
|
|
|
|
if(!this.dirty)
|
|
{
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
|
|
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
// update the uvs
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
|
|
gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
|
|
// check if a texture is dirty..
|
|
if(this.texture.baseTexture._dirty[gl.id])
|
|
{
|
|
renderSession.renderer.updateTexture(this.texture.baseTexture);
|
|
}
|
|
else
|
|
{
|
|
// bind the current texture
|
|
gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]);
|
|
}
|
|
|
|
// dont need to upload!
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
this.dirty = false;
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW);
|
|
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
// update the uvs
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW);
|
|
gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
|
|
// check if a texture is dirty..
|
|
if(this.texture.baseTexture._dirty[gl.id])
|
|
{
|
|
renderSession.renderer.updateTexture(this.texture.baseTexture);
|
|
}
|
|
else
|
|
{
|
|
gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]);
|
|
}
|
|
|
|
// dont need to upload!
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
|
|
|
|
}
|
|
//console.log(gl.TRIANGLE_STRIP)
|
|
//
|
|
//
|
|
gl.drawElements(drawMode, this.indices.length, gl.UNSIGNED_SHORT, 0);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
PIXI.Strip.prototype._renderCanvas = function(renderSession)
|
|
{
|
|
var context = renderSession.context;
|
|
|
|
var transform = this.worldTransform;
|
|
|
|
var tx = (transform.tx * renderSession.resolution) + renderSession.shakeX;
|
|
var ty = (transform.ty * renderSession.resolution) + renderSession.shakeY;
|
|
|
|
if (renderSession.roundPixels)
|
|
{
|
|
context.setTransform(transform.a, transform.b, transform.c, transform.d, tx | 0, ty | 0);
|
|
}
|
|
else
|
|
{
|
|
context.setTransform(transform.a, transform.b, transform.c, transform.d, tx, ty);
|
|
}
|
|
|
|
if (this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP)
|
|
{
|
|
this._renderCanvasTriangleStrip(context);
|
|
}
|
|
else
|
|
{
|
|
this._renderCanvasTriangles(context);
|
|
}
|
|
};
|
|
|
|
PIXI.Strip.prototype._renderCanvasTriangleStrip = function(context)
|
|
{
|
|
// draw triangles!!
|
|
var vertices = this.vertices;
|
|
var uvs = this.uvs;
|
|
|
|
var length = vertices.length / 2;
|
|
this.count++;
|
|
|
|
for (var i = 0; i < length - 2; i++) {
|
|
// draw some triangles!
|
|
var index = i * 2;
|
|
this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4));
|
|
}
|
|
};
|
|
|
|
PIXI.Strip.prototype._renderCanvasTriangles = function(context)
|
|
{
|
|
// draw triangles!!
|
|
var vertices = this.vertices;
|
|
var uvs = this.uvs;
|
|
var indices = this.indices;
|
|
|
|
var length = indices.length;
|
|
this.count++;
|
|
|
|
for (var i = 0; i < length; i += 3) {
|
|
// draw some triangles!
|
|
var index0 = indices[i] * 2, index1 = indices[i + 1] * 2, index2 = indices[i + 2] * 2;
|
|
this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2);
|
|
}
|
|
};
|
|
|
|
PIXI.Strip.prototype._renderCanvasDrawTriangle = function(context, vertices, uvs, index0, index1, index2)
|
|
{
|
|
var textureSource = this.texture.baseTexture.source;
|
|
var textureWidth = this.texture.width;
|
|
var textureHeight = this.texture.height;
|
|
|
|
var x0 = vertices[index0], x1 = vertices[index1], x2 = vertices[index2];
|
|
var y0 = vertices[index0 + 1], y1 = vertices[index1 + 1], y2 = vertices[index2 + 1];
|
|
|
|
var u0 = uvs[index0] * textureWidth, u1 = uvs[index1] * textureWidth, u2 = uvs[index2] * textureWidth;
|
|
var v0 = uvs[index0 + 1] * textureHeight, v1 = uvs[index1 + 1] * textureHeight, v2 = uvs[index2 + 1] * textureHeight;
|
|
|
|
if (this.canvasPadding > 0) {
|
|
var paddingX = this.canvasPadding / this.worldTransform.a;
|
|
var paddingY = this.canvasPadding / this.worldTransform.d;
|
|
var centerX = (x0 + x1 + x2) / 3;
|
|
var centerY = (y0 + y1 + y2) / 3;
|
|
|
|
var normX = x0 - centerX;
|
|
var normY = y0 - centerY;
|
|
|
|
var dist = Math.sqrt(normX * normX + normY * normY);
|
|
x0 = centerX + (normX / dist) * (dist + paddingX);
|
|
y0 = centerY + (normY / dist) * (dist + paddingY);
|
|
|
|
//
|
|
|
|
normX = x1 - centerX;
|
|
normY = y1 - centerY;
|
|
|
|
dist = Math.sqrt(normX * normX + normY * normY);
|
|
x1 = centerX + (normX / dist) * (dist + paddingX);
|
|
y1 = centerY + (normY / dist) * (dist + paddingY);
|
|
|
|
normX = x2 - centerX;
|
|
normY = y2 - centerY;
|
|
|
|
dist = Math.sqrt(normX * normX + normY * normY);
|
|
x2 = centerX + (normX / dist) * (dist + paddingX);
|
|
y2 = centerY + (normY / dist) * (dist + paddingY);
|
|
}
|
|
|
|
context.save();
|
|
context.beginPath();
|
|
|
|
|
|
context.moveTo(x0, y0);
|
|
context.lineTo(x1, y1);
|
|
context.lineTo(x2, y2);
|
|
|
|
context.closePath();
|
|
|
|
context.clip();
|
|
|
|
// Compute matrix transform
|
|
var delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2);
|
|
var deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2);
|
|
var deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2);
|
|
var deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2);
|
|
var deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2);
|
|
var deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2);
|
|
var deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2);
|
|
|
|
context.transform(deltaA / delta, deltaD / delta,
|
|
deltaB / delta, deltaE / delta,
|
|
deltaC / delta, deltaF / delta);
|
|
|
|
context.drawImage(textureSource, 0, 0);
|
|
context.restore();
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Renders a flat strip
|
|
*
|
|
* @method renderStripFlat
|
|
* @param strip {Strip} The Strip to render
|
|
* @private
|
|
*/
|
|
PIXI.Strip.prototype.renderStripFlat = function(strip)
|
|
{
|
|
var context = this.context;
|
|
var vertices = strip.vertices;
|
|
|
|
var length = vertices.length/2;
|
|
this.count++;
|
|
|
|
context.beginPath();
|
|
for (var i=1; i < length-2; i++)
|
|
{
|
|
// draw some triangles!
|
|
var index = i*2;
|
|
|
|
var x0 = vertices[index], x1 = vertices[index+2], x2 = vertices[index+4];
|
|
var y0 = vertices[index+1], y1 = vertices[index+3], y2 = vertices[index+5];
|
|
|
|
context.moveTo(x0, y0);
|
|
context.lineTo(x1, y1);
|
|
context.lineTo(x2, y2);
|
|
}
|
|
|
|
context.fillStyle = '#FF0000';
|
|
context.fill();
|
|
context.closePath();
|
|
};
|
|
|
|
/*
|
|
PIXI.Strip.prototype.setTexture = function(texture)
|
|
{
|
|
//TODO SET THE TEXTURES
|
|
//TODO VISIBILITY
|
|
|
|
// stop current texture
|
|
this.texture = texture;
|
|
this.width = texture.frame.width;
|
|
this.height = texture.frame.height;
|
|
this.updateFrame = true;
|
|
};
|
|
*/
|
|
|
|
/**
|
|
* When the texture is updated, this event will fire to update the scale and frame
|
|
*
|
|
* @method onTextureUpdate
|
|
* @param event
|
|
* @private
|
|
*/
|
|
|
|
PIXI.Strip.prototype.onTextureUpdate = function()
|
|
{
|
|
this.updateFrame = true;
|
|
};
|
|
|
|
/**
|
|
* Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account.
|
|
*
|
|
* @method getBounds
|
|
* @param matrix {Matrix} the transformation matrix of the sprite
|
|
* @return {Rectangle} the framing rectangle
|
|
*/
|
|
PIXI.Strip.prototype.getBounds = function(matrix)
|
|
{
|
|
var worldTransform = matrix || this.worldTransform;
|
|
|
|
var a = worldTransform.a;
|
|
var b = worldTransform.b;
|
|
var c = worldTransform.c;
|
|
var d = worldTransform.d;
|
|
var tx = worldTransform.tx;
|
|
var ty = worldTransform.ty;
|
|
|
|
var maxX = -Infinity;
|
|
var maxY = -Infinity;
|
|
|
|
var minX = Infinity;
|
|
var minY = Infinity;
|
|
|
|
var vertices = this.vertices;
|
|
for (var i = 0, n = vertices.length; i < n; i += 2)
|
|
{
|
|
var rawX = vertices[i], rawY = vertices[i + 1];
|
|
var x = (a * rawX) + (c * rawY) + tx;
|
|
var y = (d * rawY) + (b * rawX) + ty;
|
|
|
|
minX = x < minX ? x : minX;
|
|
minY = y < minY ? y : minY;
|
|
|
|
maxX = x > maxX ? x : maxX;
|
|
maxY = y > maxY ? y : maxY;
|
|
}
|
|
|
|
if (minX === -Infinity || maxY === Infinity)
|
|
{
|
|
return PIXI.EmptyRectangle;
|
|
}
|
|
|
|
var bounds = this._bounds;
|
|
|
|
bounds.x = minX;
|
|
bounds.width = maxX - minX;
|
|
|
|
bounds.y = minY;
|
|
bounds.height = maxY - minY;
|
|
|
|
// store a reference so that if this function gets called again in the render cycle we do not have to recalculate
|
|
this._currentBounds = bounds;
|
|
|
|
return bounds;
|
|
};
|
|
|
|
/**
|
|
* Different drawing buffer modes supported
|
|
*
|
|
* @property
|
|
* @type {{TRIANGLE_STRIP: number, TRIANGLES: number}}
|
|
* @static
|
|
*/
|
|
PIXI.Strip.DrawModes = {
|
|
TRIANGLE_STRIP: 0,
|
|
TRIANGLES: 1
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
* @copyright Mat Groves, Rovanion Luckey
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @class Rope
|
|
* @constructor
|
|
* @extends Strip
|
|
* @param {Texture} texture - The texture to use on the rope.
|
|
* @param {Array} points - An array of {PIXI.Point}.
|
|
*
|
|
*/
|
|
PIXI.Rope = function(texture, points)
|
|
{
|
|
PIXI.Strip.call( this, texture );
|
|
this.points = points;
|
|
|
|
this.vertices = new PIXI.Float32Array(points.length * 4);
|
|
this.uvs = new PIXI.Float32Array(points.length * 4);
|
|
this.colors = new PIXI.Float32Array(points.length * 2);
|
|
this.indices = new PIXI.Uint16Array(points.length * 2);
|
|
|
|
|
|
this.refresh();
|
|
};
|
|
|
|
|
|
// constructor
|
|
PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype );
|
|
PIXI.Rope.prototype.constructor = PIXI.Rope;
|
|
|
|
/*
|
|
* Refreshes
|
|
*
|
|
* @method refresh
|
|
*/
|
|
PIXI.Rope.prototype.refresh = function()
|
|
{
|
|
var points = this.points;
|
|
if(points.length < 1) return;
|
|
|
|
var uvs = this.uvs;
|
|
|
|
var lastPoint = points[0];
|
|
var indices = this.indices;
|
|
var colors = this.colors;
|
|
|
|
this.count-=0.2;
|
|
|
|
uvs[0] = 0;
|
|
uvs[1] = 0;
|
|
uvs[2] = 0;
|
|
uvs[3] = 1;
|
|
|
|
colors[0] = 1;
|
|
colors[1] = 1;
|
|
|
|
indices[0] = 0;
|
|
indices[1] = 1;
|
|
|
|
var total = points.length,
|
|
point, index, amount;
|
|
|
|
for (var i = 1; i < total; i++)
|
|
{
|
|
point = points[i];
|
|
index = i * 4;
|
|
// time to do some smart drawing!
|
|
amount = i / (total-1);
|
|
|
|
if(i%2)
|
|
{
|
|
uvs[index] = amount;
|
|
uvs[index+1] = 0;
|
|
|
|
uvs[index+2] = amount;
|
|
uvs[index+3] = 1;
|
|
}
|
|
else
|
|
{
|
|
uvs[index] = amount;
|
|
uvs[index+1] = 0;
|
|
|
|
uvs[index+2] = amount;
|
|
uvs[index+3] = 1;
|
|
}
|
|
|
|
index = i * 2;
|
|
colors[index] = 1;
|
|
colors[index+1] = 1;
|
|
|
|
index = i * 2;
|
|
indices[index] = index;
|
|
indices[index + 1] = index + 1;
|
|
|
|
lastPoint = point;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Updates the object transform for rendering
|
|
*
|
|
* @method updateTransform
|
|
* @private
|
|
*/
|
|
PIXI.Rope.prototype.updateTransform = function()
|
|
{
|
|
|
|
var points = this.points;
|
|
if(points.length < 1)return;
|
|
|
|
var lastPoint = points[0];
|
|
var nextPoint;
|
|
var perp = {x:0, y:0};
|
|
|
|
this.count-=0.2;
|
|
|
|
var vertices = this.vertices;
|
|
var total = points.length,
|
|
point, index, ratio, perpLength, num;
|
|
|
|
for (var i = 0; i < total; i++)
|
|
{
|
|
point = points[i];
|
|
index = i * 4;
|
|
|
|
if(i < points.length-1)
|
|
{
|
|
nextPoint = points[i+1];
|
|
}
|
|
else
|
|
{
|
|
nextPoint = point;
|
|
}
|
|
|
|
perp.y = -(nextPoint.x - lastPoint.x);
|
|
perp.x = nextPoint.y - lastPoint.y;
|
|
|
|
ratio = (1 - (i / (total-1))) * 10;
|
|
|
|
if(ratio > 1) ratio = 1;
|
|
|
|
perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y);
|
|
num = this.texture.height / 2; //(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio;
|
|
perp.x /= perpLength;
|
|
perp.y /= perpLength;
|
|
|
|
perp.x *= num;
|
|
perp.y *= num;
|
|
|
|
vertices[index] = point.x + perp.x;
|
|
vertices[index+1] = point.y + perp.y;
|
|
vertices[index+2] = point.x - perp.x;
|
|
vertices[index+3] = point.y - perp.y;
|
|
|
|
lastPoint = point;
|
|
}
|
|
|
|
PIXI.DisplayObjectContainer.prototype.updateTransform.call( this );
|
|
};
|
|
/*
|
|
* Sets the texture that the Rope will use
|
|
*
|
|
* @method setTexture
|
|
* @param texture {Texture} the texture that will be used
|
|
*/
|
|
PIXI.Rope.prototype.setTexture = function(texture)
|
|
{
|
|
// stop current texture
|
|
this.texture = texture;
|
|
//this.updateFrame = true;
|
|
};
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/
|
|
*/
|
|
|
|
/**
|
|
* A tiling sprite is a fast way of rendering a tiling image
|
|
*
|
|
* @class TilingSprite
|
|
* @extends Sprite
|
|
* @constructor
|
|
* @param texture {Texture} the texture of the tiling sprite
|
|
* @param width {Number} the width of the tiling sprite
|
|
* @param height {Number} the height of the tiling sprite
|
|
*/
|
|
PIXI.TilingSprite = function(texture, width, height)
|
|
{
|
|
PIXI.Sprite.call(this, texture);
|
|
|
|
/**
|
|
* The width of the tiling sprite
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
this._width = width || 128;
|
|
|
|
/**
|
|
* The height of the tiling sprite
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
this._height = height || 128;
|
|
|
|
/**
|
|
* The scaling of the image that is being tiled
|
|
*
|
|
* @property tileScale
|
|
* @type Point
|
|
*/
|
|
this.tileScale = new PIXI.Point(1, 1);
|
|
|
|
/**
|
|
* A point that represents the scale of the texture object
|
|
*
|
|
* @property tileScaleOffset
|
|
* @type Point
|
|
*/
|
|
this.tileScaleOffset = new PIXI.Point(1, 1);
|
|
|
|
/**
|
|
* The offset position of the image that is being tiled
|
|
*
|
|
* @property tilePosition
|
|
* @type Point
|
|
*/
|
|
this.tilePosition = new PIXI.Point();
|
|
|
|
/**
|
|
* Whether this sprite is renderable or not
|
|
*
|
|
* @property renderable
|
|
* @type Boolean
|
|
* @default true
|
|
*/
|
|
this.renderable = true;
|
|
|
|
/**
|
|
* The tint applied to the sprite. This is a hex value
|
|
*
|
|
* @property tint
|
|
* @type Number
|
|
* @default 0xFFFFFF
|
|
*/
|
|
this.tint = 0xFFFFFF;
|
|
|
|
/**
|
|
* If enabled a green rectangle will be drawn behind the generated tiling texture, allowing you to visually
|
|
* debug the texture being used.
|
|
*
|
|
* @property textureDebug
|
|
* @type Boolean
|
|
*/
|
|
this.textureDebug = false;
|
|
|
|
/**
|
|
* The blend mode to be applied to the sprite
|
|
*
|
|
* @property blendMode
|
|
* @type Number
|
|
* @default PIXI.blendModes.NORMAL;
|
|
*/
|
|
this.blendMode = PIXI.blendModes.NORMAL;
|
|
|
|
/**
|
|
* The CanvasBuffer object that the tiled texture is drawn to.
|
|
*
|
|
* @property canvasBuffer
|
|
* @type PIXI.CanvasBuffer
|
|
*/
|
|
this.canvasBuffer = null;
|
|
|
|
/**
|
|
* An internal Texture object that holds the tiling texture that was generated from TilingSprite.texture.
|
|
*
|
|
* @property tilingTexture
|
|
* @type PIXI.Texture
|
|
*/
|
|
this.tilingTexture = null;
|
|
|
|
/**
|
|
* The Context fill pattern that is used to draw the TilingSprite in Canvas mode only (will be null in WebGL).
|
|
*
|
|
* @property tilePattern
|
|
* @type PIXI.Texture
|
|
*/
|
|
this.tilePattern = null;
|
|
|
|
/**
|
|
* If true the TilingSprite will run generateTexture on its **next** render pass.
|
|
* This is set by the likes of Phaser.LoadTexture.setFrame.
|
|
*
|
|
* @property refreshTexture
|
|
* @type Boolean
|
|
* @default true
|
|
*/
|
|
this.refreshTexture = true;
|
|
|
|
this.frameWidth = 0;
|
|
this.frameHeight = 0;
|
|
|
|
};
|
|
|
|
PIXI.TilingSprite.prototype = Object.create(PIXI.Sprite.prototype);
|
|
PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite;
|
|
|
|
PIXI.TilingSprite.prototype.setTexture = function(texture)
|
|
{
|
|
if (this.texture !== texture)
|
|
{
|
|
this.texture = texture;
|
|
this.refreshTexture = true;
|
|
this.cachedTint = 0xFFFFFF;
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the WebGL renderer
|
|
*
|
|
* @method _renderWebGL
|
|
* @param renderSession {RenderSession}
|
|
* @private
|
|
*/
|
|
PIXI.TilingSprite.prototype._renderWebGL = function(renderSession)
|
|
{
|
|
if (!this.visible || !this.renderable || this.alpha === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.spriteBatch.stop();
|
|
renderSession.maskManager.pushMask(this.mask, renderSession);
|
|
renderSession.spriteBatch.start();
|
|
}
|
|
|
|
if (this._filters)
|
|
{
|
|
renderSession.spriteBatch.flush();
|
|
renderSession.filterManager.pushFilter(this._filterBlock);
|
|
}
|
|
|
|
if (this.refreshTexture)
|
|
{
|
|
this.generateTilingTexture(true, renderSession);
|
|
|
|
if (this.tilingTexture)
|
|
{
|
|
if (this.tilingTexture.needsUpdate)
|
|
{
|
|
this.tilingTexture.baseTexture.textureIndex = this.texture.baseTexture.textureIndex;
|
|
renderSession.renderer.updateTexture(this.tilingTexture.baseTexture);
|
|
this.tilingTexture.needsUpdate = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
renderSession.spriteBatch.renderTilingSprite(this);
|
|
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderWebGL(renderSession);
|
|
}
|
|
var restartBatch = false;
|
|
if (this._filters)
|
|
{
|
|
restartBatch = true;
|
|
renderSession.spriteBatch.stop();
|
|
renderSession.filterManager.popFilter();
|
|
}
|
|
|
|
if (this._mask)
|
|
{
|
|
if (!restartBatch)
|
|
renderSession.spriteBatch.stop();
|
|
renderSession.maskManager.popMask(this._mask, renderSession);
|
|
}
|
|
if (restartBatch)
|
|
renderSession.spriteBatch.start();
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders the object using the Canvas renderer
|
|
*
|
|
* @method _renderCanvas
|
|
* @param renderSession {RenderSession}
|
|
* @private
|
|
*/
|
|
PIXI.TilingSprite.prototype._renderCanvas = function(renderSession)
|
|
{
|
|
if (!this.visible || !this.renderable || this.alpha === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var context = renderSession.context;
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.maskManager.pushMask(this._mask, renderSession);
|
|
}
|
|
|
|
context.globalAlpha = this.worldAlpha;
|
|
|
|
var wt = this.worldTransform;
|
|
var resolution = renderSession.resolution;
|
|
var tx = (wt.tx * resolution) + renderSession.shakeX;
|
|
var ty = (wt.ty * resolution) + renderSession.shakeY;
|
|
|
|
context.setTransform(wt.a * resolution, wt.b * resolution, wt.c * resolution, wt.d * resolution, tx, ty);
|
|
|
|
if (this.refreshTexture)
|
|
{
|
|
this.generateTilingTexture(false, renderSession);
|
|
|
|
if (this.tilingTexture)
|
|
{
|
|
this.tilePattern = context.createPattern(this.tilingTexture.baseTexture.source, 'repeat');
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
var sessionBlendMode = renderSession.currentBlendMode;
|
|
|
|
// Check blend mode
|
|
if (this.blendMode !== renderSession.currentBlendMode)
|
|
{
|
|
renderSession.currentBlendMode = this.blendMode;
|
|
context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode];
|
|
}
|
|
|
|
var tilePosition = this.tilePosition;
|
|
var tileScale = this.tileScale;
|
|
|
|
tilePosition.x %= this.tilingTexture.baseTexture.width;
|
|
tilePosition.y %= this.tilingTexture.baseTexture.height;
|
|
|
|
// Translate
|
|
context.scale(tileScale.x, tileScale.y);
|
|
context.translate(tilePosition.x + (this.anchor.x * -this._width), tilePosition.y + (this.anchor.y * -this._height));
|
|
|
|
context.fillStyle = this.tilePattern;
|
|
|
|
var tx = -tilePosition.x;
|
|
var ty = -tilePosition.y;
|
|
var tw = this._width / tileScale.x;
|
|
var th = this._height / tileScale.y;
|
|
|
|
// Allow for pixel rounding
|
|
if (renderSession.roundPixels)
|
|
{
|
|
tx |= 0;
|
|
ty |= 0;
|
|
tw |= 0;
|
|
th |= 0;
|
|
}
|
|
|
|
context.fillRect(tx, ty, tw, th);
|
|
|
|
// Translate back again
|
|
context.scale(1 / tileScale.x, 1 / tileScale.y);
|
|
context.translate(-tilePosition.x + (this.anchor.x * this._width), -tilePosition.y + (this.anchor.y * this._height));
|
|
|
|
if (this._mask)
|
|
{
|
|
renderSession.maskManager.popMask(renderSession);
|
|
}
|
|
|
|
for (var i = 0; i < this.children.length; i++)
|
|
{
|
|
this.children[i]._renderCanvas(renderSession);
|
|
}
|
|
|
|
// Reset blend mode
|
|
if (sessionBlendMode !== this.blendMode)
|
|
{
|
|
renderSession.currentBlendMode = sessionBlendMode;
|
|
context.globalCompositeOperation = PIXI.blendModesCanvas[sessionBlendMode];
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* When the texture is updated, this event will fire to update the scale and frame
|
|
*
|
|
* @method onTextureUpdate
|
|
* @param event
|
|
* @private
|
|
*/
|
|
PIXI.TilingSprite.prototype.onTextureUpdate = function()
|
|
{
|
|
// overriding the sprite version of this!
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @method generateTilingTexture
|
|
*
|
|
* @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two
|
|
* @param renderSession {RenderSession}
|
|
*/
|
|
PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo, renderSession)
|
|
{
|
|
if (!this.texture.baseTexture.hasLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var texture = this.texture;
|
|
var frame = texture.frame;
|
|
|
|
var targetWidth = this._frame.sourceSizeW || this._frame.width;
|
|
var targetHeight = this._frame.sourceSizeH || this._frame.height;
|
|
|
|
var dx = 0;
|
|
var dy = 0;
|
|
|
|
if (this._frame.trimmed)
|
|
{
|
|
dx = this._frame.spriteSourceSizeX;
|
|
dy = this._frame.spriteSourceSizeY;
|
|
}
|
|
|
|
if (forcePowerOfTwo)
|
|
{
|
|
targetWidth = PIXI.getNextPowerOfTwo(targetWidth);
|
|
targetHeight = PIXI.getNextPowerOfTwo(targetHeight);
|
|
}
|
|
|
|
if (this.canvasBuffer)
|
|
{
|
|
this.canvasBuffer.resize(targetWidth, targetHeight);
|
|
this.tilingTexture.baseTexture.width = targetWidth;
|
|
this.tilingTexture.baseTexture.height = targetHeight;
|
|
this.tilingTexture.needsUpdate = true;
|
|
}
|
|
else
|
|
{
|
|
this.canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight);
|
|
this.tilingTexture = PIXI.Texture.fromCanvas(this.canvasBuffer.canvas);
|
|
this.tilingTexture.isTiling = true;
|
|
this.tilingTexture.needsUpdate = true;
|
|
}
|
|
|
|
if (this.textureDebug)
|
|
{
|
|
this.canvasBuffer.context.strokeStyle = '#00ff00';
|
|
this.canvasBuffer.context.strokeRect(0, 0, targetWidth, targetHeight);
|
|
}
|
|
|
|
// If a sprite sheet we need this:
|
|
var w = texture.crop.width;
|
|
var h = texture.crop.height;
|
|
|
|
if (w !== targetWidth || h !== targetHeight)
|
|
{
|
|
w = targetWidth;
|
|
h = targetHeight;
|
|
}
|
|
|
|
this.canvasBuffer.context.drawImage(texture.baseTexture.source,
|
|
texture.crop.x,
|
|
texture.crop.y,
|
|
texture.crop.width,
|
|
texture.crop.height,
|
|
dx,
|
|
dy,
|
|
w,
|
|
h);
|
|
|
|
this.tileScaleOffset.x = frame.width / targetWidth;
|
|
this.tileScaleOffset.y = frame.height / targetHeight;
|
|
|
|
this.refreshTexture = false;
|
|
|
|
this.tilingTexture.baseTexture._powerOf2 = true;
|
|
|
|
};
|
|
|
|
/**
|
|
* Returns the framing rectangle of the sprite as a PIXI.Rectangle object
|
|
*
|
|
* @method getBounds
|
|
* @return {Rectangle} the framing rectangle
|
|
*/
|
|
PIXI.TilingSprite.prototype.getBounds = function()
|
|
{
|
|
var width = this._width;
|
|
var height = this._height;
|
|
|
|
var w0 = width * (1-this.anchor.x);
|
|
var w1 = width * -this.anchor.x;
|
|
|
|
var h0 = height * (1-this.anchor.y);
|
|
var h1 = height * -this.anchor.y;
|
|
|
|
var worldTransform = this.worldTransform;
|
|
|
|
var a = worldTransform.a;
|
|
var b = worldTransform.b;
|
|
var c = worldTransform.c;
|
|
var d = worldTransform.d;
|
|
var tx = worldTransform.tx;
|
|
var ty = worldTransform.ty;
|
|
|
|
var x1 = a * w1 + c * h1 + tx;
|
|
var y1 = d * h1 + b * w1 + ty;
|
|
|
|
var x2 = a * w0 + c * h1 + tx;
|
|
var y2 = d * h1 + b * w0 + ty;
|
|
|
|
var x3 = a * w0 + c * h0 + tx;
|
|
var y3 = d * h0 + b * w0 + ty;
|
|
|
|
var x4 = a * w1 + c * h0 + tx;
|
|
var y4 = d * h0 + b * w1 + ty;
|
|
|
|
var maxX = -Infinity;
|
|
var maxY = -Infinity;
|
|
|
|
var minX = Infinity;
|
|
var minY = Infinity;
|
|
|
|
minX = x1 < minX ? x1 : minX;
|
|
minX = x2 < minX ? x2 : minX;
|
|
minX = x3 < minX ? x3 : minX;
|
|
minX = x4 < minX ? x4 : minX;
|
|
|
|
minY = y1 < minY ? y1 : minY;
|
|
minY = y2 < minY ? y2 : minY;
|
|
minY = y3 < minY ? y3 : minY;
|
|
minY = y4 < minY ? y4 : minY;
|
|
|
|
maxX = x1 > maxX ? x1 : maxX;
|
|
maxX = x2 > maxX ? x2 : maxX;
|
|
maxX = x3 > maxX ? x3 : maxX;
|
|
maxX = x4 > maxX ? x4 : maxX;
|
|
|
|
maxY = y1 > maxY ? y1 : maxY;
|
|
maxY = y2 > maxY ? y2 : maxY;
|
|
maxY = y3 > maxY ? y3 : maxY;
|
|
maxY = y4 > maxY ? y4 : maxY;
|
|
|
|
var bounds = this._bounds;
|
|
|
|
bounds.x = minX;
|
|
bounds.width = maxX - minX;
|
|
|
|
bounds.y = minY;
|
|
bounds.height = maxY - minY;
|
|
|
|
// store a reference so that if this function gets called again in the render cycle we do not have to recalculate
|
|
this._currentBounds = bounds;
|
|
|
|
return bounds;
|
|
};
|
|
|
|
PIXI.TilingSprite.prototype.destroy = function () {
|
|
|
|
PIXI.Sprite.prototype.destroy.call(this);
|
|
|
|
if (this.canvasBuffer)
|
|
{
|
|
this.canvasBuffer.destroy();
|
|
this.canvasBuffer = null;
|
|
}
|
|
|
|
this.tileScale = null;
|
|
this.tileScaleOffset = null;
|
|
this.tilePosition = null;
|
|
|
|
if (this.tilingTexture)
|
|
{
|
|
this.tilingTexture.destroy(true);
|
|
this.tilingTexture = null;
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* The width of the sprite, setting this will actually modify the scale to achieve the value set
|
|
*
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
Object.defineProperty(PIXI.TilingSprite.prototype, 'width', {
|
|
|
|
get: function() {
|
|
return this._width;
|
|
},
|
|
|
|
set: function(value) {
|
|
this._width = value;
|
|
}
|
|
|
|
});
|
|
|
|
/**
|
|
* The height of the TilingSprite, setting this will actually modify the scale to achieve the value set
|
|
*
|
|
* @property height
|
|
* @type Number
|
|
*/
|
|
Object.defineProperty(PIXI.TilingSprite.prototype, 'height', {
|
|
|
|
get: function() {
|
|
return this._height;
|
|
},
|
|
|
|
set: function(value) {
|
|
this._height = value;
|
|
}
|
|
|
|
});
|
|
|
|
/**
|
|
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
*/
|
|
|
|
if (typeof exports !== 'undefined') {
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
exports = module.exports = PIXI;
|
|
}
|
|
exports.PIXI = PIXI;
|
|
} else if (typeof define !== 'undefined' && define.amd) {
|
|
define('PIXI', (function() { return root.PIXI = PIXI; })() );
|
|
} else {
|
|
root.PIXI = PIXI;
|
|
}
|
|
|
|
return PIXI;
|
|
}).call(this); |