/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* Standard Image and Sprite Shader.
*
* @class Phaser.Renderer.WebGL.Batch.Image
* @constructor
* @param {Phaser.Renderer.WebGL} renderer - The WebGL Renderer.
*/
Phaser.Renderer.WebGL.Batch.FX = function (manager, batchSize)
{
    //  Vertex Data Size is calculated by adding together:
    //
    //  Position (vec2) = 4 * 2 = 8 bytes
    //  UV (vec2) = 4 * 2 = 8 bytes
    //  Texture Index (float) = 4 bytes
    //  Tint Color (float) = 4 bytes
    //  BG Color (float) = 4 bytes
    //
    //  Total: 28 bytes (per vert) * 4 (4 verts per quad) (= 112 bytes) * maxSize (usually 2000) = 224 kilobytes sent to the GPU every frame

    var vertSize = (4 * 2) + (4 * 2) + (4) + (4) + (4);

    Phaser.Renderer.WebGL.Batch.call(this, manager, batchSize, vertSize);

    this.type = 4;

    //  View on the vertices as a Float32Array
    this.positions = new Float32Array(this.vertices);

    //  View on the vertices as a Uint32Array
    this.colors = new Uint32Array(this.vertices);

    //  Attributes and Uniforms specific to this Batch Shader

    //  @type {GLint}
    this.aVertexPosition;

    //  @type {GLint}
    this.aTextureCoord;

    //  @type {GLint}
    this.aTextureIndex;

    //  @type {GLint}
    this.aTintColor;

    //  @type {GLint}
    this.aBgColor;

    //  @type {WebGLUniformLocation}
    this.uSampler;

    //  @type {WebGLUniformLocation}
    this.projectionVector;

    //  @type {WebGLUniformLocation}
    this.offsetVector;
};

Phaser.Renderer.WebGL.Batch.FX.prototype = Object.create(Phaser.Renderer.WebGL.Batch.prototype);

Phaser.Renderer.WebGL.Batch.FX.prototype.constructor = Phaser.Renderer.WebGL.Batch.FX;

Phaser.Renderer.WebGL.Batch.FX.prototype.init = function ()
{
    this.gl = this.renderer.gl;

    this.vertexSrc = [
        'attribute vec2 aVertexPosition;',
        'attribute vec2 aTextureCoord;',
        'attribute float aTextureIndex;',
        'attribute vec4 aTintColor;',
        'attribute vec4 aBgColor;',

        'uniform vec2 projectionVector;',
        'uniform vec2 offsetVector;',

        'varying vec2 vTextureCoord;',
        'varying vec4 vTintColor;',
        'varying vec4 vBgColor;',
        '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;', // pass the texture coordinate to the fragment shader, the GPU will interpolate the points
        '   vTintColor = vec4(aTintColor.rgb * aTintColor.a, aTintColor.a);',
        '   vBgColor = aBgColor;',
        '   vTextureIndex = aTextureIndex;',
        '}'
    ];

    this.fragmentSrc = [
        'precision lowp float;',

        'varying vec2 vTextureCoord;', // the texture coords passed in from the vertex shader
        'varying vec4 vTintColor;', //  the color value passed in from the vertex shader (texture color + alpha + tint)
        'varying vec4 vBgColor;', //  the bg color value passed in from the vertex shader
        'varying float vTextureIndex;',

        'uniform sampler2D uSampler;', // our texture

        'void main(void) {',
        '   gl_FragColor = texture2D(uSampler, vTextureCoord);',
        '   gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126 * gl_FragColor.r + 0.7152 * gl_FragColor.g + 0.0722 * gl_FragColor.b), 1.0);',
        '}'
    ];

    //  Compile the Shader
    this.program = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc);

    //  Our static index buffer, calculated once at the start of our game

    //  This contains the indices data for the quads.
    //
    //  A quad is made up of 2 triangles (A and B in the image below)
    //
    //  0 = Top Left
    //  1 = Top Right
    //  2 = Bottom Right
    //  3 = Bottom Left
    //
    //  0----1
    //  |\  A|
    //  | \  |
    //  |  \ |
    //  | B \|
    //  |    \
    //  3----2
    //
    //  Because triangles A and B share 2 points (0 and 2) the vertex buffer only stores
    //  4 sets of data (top-left, top-right, bottom-left and bottom-right), which is why
    //  the indices offsets uses the j += 4 iteration. Indices array has to contain 3
    //  entries for every triangle (so 6 for every quad), but our vertex data compacts
    //  that down, as we don't want to fill it with loads of DUPLICATE data, so the
    //  indices array is a look-up table, telling WebGL where in the vertex buffer to look
    //  for that triangles indice data.

    //  batchSize * vertSize = 2000 * 6 (because we store 6 pieces of vertex data per triangle)
    //  and up to a maximum of 2000 entries in the batch

    for (var i = 0, j = 0; i < (this.maxSize * this.vertSize); i += 6, j += 4)
    {
        //  Triangle 1
        this.indices[i + 0] = j + 0;    //  Top Left
        this.indices[i + 1] = j + 1;    //  Top Right
        this.indices[i + 2] = j + 2;    //  Bottom Right

        //  Triangle 2
        this.indices[i + 3] = j + 0;    //  Top Left
        this.indices[i + 4] = j + 2;    //  Bottom Right
        this.indices[i + 5] = j + 3;    //  Bottom Left
    }

    var gl = this.gl;

    //  Create indices buffer
    this.indexBuffer = gl.createBuffer();

    //  Bind it
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);

    //  Set the source of the buffer data (this.indices array)
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);

    //  Create Vertex Data buffer
    this.vertexBuffer = gl.createBuffer();

    //  Bind it
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

    //  Set the source of the buffer data (this.vertices array)
    gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);

};

Phaser.Renderer.WebGL.Batch.FX.prototype.bindShader = function ()
{
    var gl = this.gl;
    var program = this.program;
    var vertSize = this.vertSize;

    //  Set Shader
    gl.useProgram(program);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);

    //  Get and store the attributes

    //  vertex position
    this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
    gl.enableVertexAttribArray(this.aVertexPosition);

    //  texture coordinate
    this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
    gl.enableVertexAttribArray(this.aTextureCoord);

    //  texture index
    this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex');
    gl.enableVertexAttribArray(this.aTextureIndex);

    //  tint / pixel color
    this.aTintColor = gl.getAttribLocation(program, 'aTintColor');
    gl.enableVertexAttribArray(this.aTintColor);

    //  background pixel color
    this.aBgColor = gl.getAttribLocation(program, 'aBgColor');
    gl.enableVertexAttribArray(this.aBgColor);

    //  The projection vector (middle of the game world)
    this.projectionVector = gl.getUniformLocation(program, 'projectionVector');

    //  The offset vector (camera shake)
    this.offsetVector = gl.getUniformLocation(program, 'offsetVector');

    //  The Texture Sampler
    this.uSampler = gl.getUniformLocation(this.program, 'uSampler');

    //  Set the projection vector. Defaults to the middle of the Game World, on negative y.
    //  I.e. if the world is 800x600 then the projection vector is 400 x -300
    gl.uniform2f(this.projectionVector, this.renderer.projection.x, this.renderer.projection.y);

    //  Set the offset vector.
    gl.uniform2f(this.offsetVector, this.renderer.offset.x, this.renderer.offset.y);

    //  The Vertex Position (x/y)
    //  2 FLOATS, 2 * 4 = 8 bytes. Index pos: 0 to 7
    //  final argument = the offset within the vertex input
    gl.vertexAttribPointer(this.aVertexPosition, 2, gl.FLOAT, false, vertSize, 0);

    //  The Texture Coordinate (uvx/uvy)
    //  2 FLOATS, 2 * 4 = 8 bytes. Index pos: 8 to 15
    gl.vertexAttribPointer(this.aTextureCoord, 2, gl.FLOAT, false, vertSize, 8);

    //  Texture Index
    //  1 FLOAT, 4 bytes. Index pos: 16 to 19
    gl.vertexAttribPointer(this.aTextureIndex, 1, gl.FLOAT, false, vertSize, 16);

    //  Tint color
    //  4 UNSIGNED BYTES, 4 bytes. Index pos: 20 to 23
    //  Attributes will be interpreted as unsigned bytes and normalized
    gl.vertexAttribPointer(this.aTintColor, 4, gl.UNSIGNED_BYTE, true, vertSize, 20);

    //  Background Color
    //  4 UNSIGNED BYTES, 4 bytes. Index pos: 24 to 27
    //  Attributes will be interpreted as unsigned bytes and normalized
    gl.vertexAttribPointer(this.aBgColor, 4, gl.UNSIGNED_BYTE, true, vertSize, 24);
};

Phaser.Renderer.WebGL.Batch.FX.prototype.add = function (verts, uvs, textureIndex, alpha, tintColors, bgColors)
{
    //  These are TypedArray Views into the vertices ArrayBuffer
    var colors = this.colors;
    var positions = this.positions;

    var i = this._i;

    //  Top Left vert (xy, uv, color)
    positions[i++] = verts.x0;
    positions[i++] = verts.y0;
    positions[i++] = uvs.x0;
    positions[i++] = uvs.y0;
    positions[i++] = textureIndex;
    colors[i++] = tintColors.topLeft + alpha;
    colors[i++] = bgColors.topLeft;

    //  Top Right vert (xy, uv, color)
    positions[i++] = verts.x1;
    positions[i++] = verts.y1;
    positions[i++] = uvs.x1;
    positions[i++] = uvs.y1;
    positions[i++] = textureIndex;
    colors[i++] = tintColors.topRight + alpha;
    colors[i++] = bgColors.topRight;

    //  Bottom Right vert (xy, uv, color)
    positions[i++] = verts.x2;
    positions[i++] = verts.y2;
    positions[i++] = uvs.x2;
    positions[i++] = uvs.y2;
    positions[i++] = textureIndex;
    colors[i++] = tintColors.bottomRight + alpha;
    colors[i++] = bgColors.bottomRight;

    //  Bottom Left vert (xy, uv, color)
    positions[i++] = verts.x3;
    positions[i++] = verts.y3;
    positions[i++] = uvs.x3;
    positions[i++] = uvs.y3;
    positions[i++] = textureIndex;
    colors[i++] = tintColors.bottomLeft + alpha;
    colors[i++] = bgColors.bottomLeft;

    this._i = i;

    this.size++;
};

Phaser.Renderer.WebGL.Batch.FX.prototype.flush = function ()
{
    if (this.size === 0)
    {
        return;
    }

    var gl = this.gl;

    //  Upload the vertex data to the GPU - is this cheaper (overall) than creating a new TypedArray view?
    //  The tradeoff is sending 224KB of data to the GPU every frame, even if most of it is empty should the
    //  batch be only slightly populated, vs. the creation of a new TypedArray view and its corresponding gc every frame.

    if (this.size > this.halfSize)
    {
        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
    }
    else
    {
        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

        this.view = this.positions.subarray(0, this.size * this.vertSize);

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.view);
    }

    gl.drawElements(gl.TRIANGLES, this.size * 6, gl.UNSIGNED_SHORT, 0);

    this.renderer.drawCount++;

    //  Reset the batch
    this.size = 0;

    this._i = 0;
};

Phaser.Renderer.WebGL.Batch.FX.prototype.destroy = function ()
{
    this.vertices = null;
    this.indices = null;
    this.view = null;

    this.gl.deleteBuffer(this.vertexBuffer);
    this.gl.deleteBuffer(this.indexBuffer);

    this.renderer.deleteProgram(this.program);

    this.renderer = null;

    this.gl = null;

    this.manager = null;
};


var width = this.texture.source[0].width;

var height = this.texture.source[0].height;

keep keep keep replace replace keep keep keep keep

var height = tileset.image.get().height;

var Class = require('../../../utils/Class');
var GameObject = require('../../GameObject');
var Components = require('../../components');
var DynamicTilemapLayerRender = require('./DynamicTilemapLayerRender');
var TilemapComponents = require('../components');

var DynamicTilemapLayer = new Class({

    Extends: GameObject,

    Mixins: [
        Components.Alpha,
        Components.BlendMode,
        Components.Flip,
        Components.GetBounds,
        Components.Origin,
        Components.RenderTarget,
        Components.ScaleMode,
        Components.Size,
        Components.Texture,
        Components.Transform,
        Components.Visible,
        Components.ScrollFactor,
        DynamicTilemapLayerRender
    ],

    initialize:

    /**
     * A DynamicTilemapLayer is a game object that renders LayerData from a Tilemap. A
     * DynamicTilemapLayer can only render tiles from a single tileset.
     *
     * A DynamicTilemapLayer trades some speed for being able to apply powerful effects. Unlike a
     * StaticTilemapLayer, you can apply per-tile effects like tint or alpha, and you can change the
     * tiles in a DynamicTilemapLayer. Use this over a StaticTilemapLayer when you need those
     * features.
     *
     * @class DynamicTilemapLayer
     * @constructor
     *
     * @param {Scene} scene - [description]
     * @param {Tilemap} tilemap - The Tilemap this layer is a part of.
     * @param {integer} layerIndex - The index of the LayerData associated with this layer.
     * @param {Tileset} tileset - The tileset used to render the tiles in this layer.
     * @param {number} [x=0] - The world x position where the top left of this layer will be placed.
     * @param {number} [y=0] - The world y position where the top left of this layer will be placed.
     */
    function DynamicTilemapLayer (scene, tilemap, layerIndex, tileset, x, y)
    {
        GameObject.call(this, scene, 'DynamicTilemapLayer');

        /**
         * Used internally by physics system to perform fast type checks.
         * @property {boolean} isTilemap
         * @readonly
         */
        this.isTilemap = true;

        /**
         * The Tilemap that this layer is a part of.
         * @property {Tilemap} map
         */
        this.map = tilemap;

        /**
         * The index of the LayerData associated with this layer.
         * @property {integer} layerIndex
         */
        this.layerIndex = layerIndex;

        /**
         * The LayerData associated with this layer. LayerData can only be associated with one
         * tilemap layer.
         * @property {LayerData} layerIndex
         */
        this.layer = tilemap.layers[layerIndex];
        this.layer.tilemapLayer = this; // Link the LayerData with this static tilemap layer

        /**
         * The Tileset associated with this layer. A tilemap layer can only render from one Tileset.
         * @property {Tileset} tileset
         */
        this.tileset = tileset;

        /**
         * Used internally with the canvas render. This holds the tiles that are visible within the
         * camera.
         * @property {Tileset} culledTiles
         */
        this.culledTiles = [];

        this.setAlpha(this.layer.alpha);
        this.setTexture(tileset.image.key);
        this.setPosition(x, y);
        this.setSizeToFrame();
        this.setOrigin();
        this.setSize(this.layer.tileWidth * this.layer.width, this.layer.tileHeight * this.layer.height);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    calculateFacesWithin: function (tileX, tileY, width, height)
    {
        TilemapComponents.CalculateFacesWithin(tileX, tileY, width, height, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {Sprite[]}
     */
    createFromTiles: function (indexes, replacements, spriteConfig, scene, camera)
    {
        return TilemapComponents.CreateFromTiles(indexes, replacements, spriteConfig, scene, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    cull: function (camera)
    {
        return TilemapComponents.CullTiles(this.layer, camera, this.culledTiles);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    copy: function (srcTileX, srcTileY, width, height, destTileX, destTileY, recalculateFaces)
    {
        TilemapComponents.Copy(srcTileX, srcTileY, width, height, destTileX, destTileY, recalculateFaces, this.layer);
        return this;
    },

    /**
    * Destroys this DynamicTilemapLayer and removes its link to the associated LayerData.
    *
    * @method Phaser.TilemapLayer#destroy
    */
    destroy: function ()
    {
        // Uninstall this layer only if it is still installed on the LayerData object
        if (this.layer.tilemapLayer === this)
        {
            this.layer.tilemapLayer = undefined;
        }
        this.map = undefined;
        this.layer = undefined;
        this.tileset = undefined;
        this.culledTiles.length = 0;
        GameObject.prototype.destroy.call(this);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    fill: function (index, tileX, tileY, width, height, recalculateFaces)
    {
        TilemapComponents.Fill(index, tileX, tileY, width, height, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    filterTiles: function (callback, context, tileX, tileY, width, height, filteringOptions)
    {
        return TilemapComponents.FilterTiles(callback, context, tileX, tileY, width, height, filteringOptions, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    findByIndex: function (findIndex, skip, reverse)
    {
        return TilemapComponents.FindByIndex(findIndex, skip, reverse, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile|null}
     */
    findTile: function (callback, context, tileX, tileY, width, height, filteringOptions)
    {
        return TilemapComponents.FindTile(callback, context, tileX, tileY, width, height, filteringOptions, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    forEachTile: function (callback, context, tileX, tileY, width, height, filteringOptions)
    {
        TilemapComponents.ForEachTile(callback, context, tileX, tileY, width, height, filteringOptions, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    getTileAt: function (tileX, tileY, nonNull)
    {
        return TilemapComponents.GetTileAt(tileX, tileY, nonNull, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    getTileAtWorldXY: function (worldX, worldY, nonNull, camera)
    {
        return TilemapComponents.GetTileAtWorldXY(worldX, worldY, nonNull, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    getTilesWithin: function (tileX, tileY, width, height, filteringOptions)
    {
        return TilemapComponents.GetTilesWithin(tileX, tileY, width, height, filteringOptions, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    getTilesWithinShape: function (shape, filteringOptions, camera)
    {
        return TilemapComponents.GetTilesWithinShape(shape, filteringOptions, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    getTilesWithinWorldXY: function (worldX, worldY, width, height, filteringOptions, camera)
    {
        return TilemapComponents.GetTilesWithinWorldXY(worldX, worldY, width, height, filteringOptions, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {boolean}
     */
    hasTileAt: function (tileX, tileY)
    {
        return TilemapComponents.HasTileAt(tileX, tileY, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {boolean}
     */
    hasTileAtWorldXY: function (worldX, worldY, camera)
    {
        return TilemapComponents.HasTileAtWorldXY(worldX, worldY, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    putTileAt: function (tile, tileX, tileY, recalculateFaces)
    {
        return TilemapComponents.PutTileAt(tile, tileX, tileY, recalculateFaces, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    putTileAtWorldXY: function (tile, worldX, worldY, recalculateFaces, camera)
    {
        return TilemapComponents.PutTileAtWorldXY(tile, worldX, worldY, recalculateFaces, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    putTilesAt: function (tilesArray, tileX, tileY, recalculateFaces)
    {
        TilemapComponents.PutTilesAt(tilesArray, tileX, tileY, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    randomize: function (tileX, tileY, width, height, indexes)
    {
        TilemapComponents.Randomize(tileX, tileY, width, height, indexes, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    removeTileAt: function (tileX, tileY, replaceWithNull, recalculateFaces)
    {
        return TilemapComponents.RemoveTileAt(tileX, tileY, replaceWithNull, recalculateFaces, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    removeTileAtWorldXY: function (worldX, worldY, replaceWithNull, recalculateFaces, camera)
    {
        return TilemapComponents.RemoveTileAtWorldXY(worldX, worldY, replaceWithNull, recalculateFaces, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    renderDebug: function (graphics, styleConfig)
    {
        TilemapComponents.RenderDebug(graphics, styleConfig, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    replaceByIndex: function (findIndex, newIndex, tileX, tileY, width, height)
    {
        TilemapComponents.ReplaceByIndex(findIndex, newIndex, tileX, tileY, width, height, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setCollision: function (indexes, collides, recalculateFaces)
    {
        TilemapComponents.SetCollision(indexes, collides, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setCollisionBetween: function (start, stop, collides, recalculateFaces)
    {
        TilemapComponents.SetCollisionBetween(start, stop, collides, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setCollisionByExclusion: function (indexes, collides, recalculateFaces)
    {
        TilemapComponents.SetCollisionByExclusion(indexes, collides, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setTileIndexCallback: function (indexes, callback, callbackContext)
    {
        TilemapComponents.SetTileIndexCallback(indexes, callback, callbackContext, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setTileLocationCallback: function (tileX, tileY, width, height, callback, callbackContext)
    {
        TilemapComponents.SetTileLocationCallback(tileX, tileY, width, height, callback, callbackContext, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    shuffle: function (tileX, tileY, width, height)
    {
        TilemapComponents.Shuffle(tileX, tileY, width, height, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    swapByIndex: function (indexA, indexB, tileX, tileY, width, height)
    {
        TilemapComponents.SwapByIndex(indexA, indexB, tileX, tileY, width, height, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    tileToWorldX: function (tileX, camera)
    {
        return TilemapComponents.TileToWorldX(tileX, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    tileToWorldY: function (tileY, camera)
    {
        return TilemapComponents.TileToWorldY(tileY, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Vector2}
     */
    tileToWorldXY: function (tileX, tileY, point, camera)
    {
        return TilemapComponents.TileToWorldXY(tileX, tileY, point, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    weightedRandomize: function (tileX, tileY, width, height, weightedIndexes)
    {
        TilemapComponents.WeightedRandomize(tileX, tileY, width, height, weightedIndexes, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    worldToTileX: function (worldX, snapToFloor, camera)
    {
        return TilemapComponents.WorldToTileX(worldX, snapToFloor, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    worldToTileY: function (worldY, snapToFloor, camera)
    {
        return TilemapComponents.WorldToTileY(worldY, snapToFloor, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Vector}
     */
    worldToTileXY: function (worldX, worldY, snapToFloor, point, camera)
    {
        return TilemapComponents.WorldToTileXY(worldX, worldY, snapToFloor, point, camera, this.layer);
    }

});

module.exports = DynamicTilemapLayer;


var GetFastValue = require('../../../../utils/object/GetFastValue');

var ParseImageLayers = function (json)
{
    var images = [];

    for (var i = 0; i < json.layers.length; i++)
    {
        if (json.layers[i].type !== 'imagelayer')
        {
            continue;
        }

        var curi = json.layers[i];

        images.push({
            name: curi.name,
            image: curi.image,
            x: GetFastValue(curi, 'offsetx', 0) + curi.x,
            y: GetFastValue(curi, 'offsety', 0) + curi.y,
            alpha: curi.opacity,
            visible: curi.visible,
            properties: GetFastValue(curi, 'properties', {})
        });
    }

    return images;
};

module.exports = ParseImageLayers;


var Class = require('../../utils/Class');
var Factory = require('./Factory');
var GetValue = require('../../utils/object/GetValue');
var MatterAttractors = require('./lib/plugins/MatterAttractors');
var MatterLib = require('./lib/core/Matter');
var MatterWrap = require('./lib/plugins/MatterWrap');
var Plugin = require('./lib/core/Plugin');
var World = require('./World');

var Matter = new Class({

    initialize:

    //  Referenced from the Scene PhysicsManager as `system`

    function Matter (physicsManager, config)
    {
        this.config = config;

        physicsManager.world = new World(physicsManager.scene, config);

        physicsManager.add = new Factory(physicsManager.world);

        //  Matter plugins

        if (GetValue(config, 'plugins.attractors', false))
        {
            Plugin.register(MatterAttractors);
            Plugin.use(MatterLib, MatterAttractors);
        }

        if (GetValue(config, 'plugins.wrap', false))
        {
            Plugin.register(MatterWrap);
            Plugin.use(MatterLib, MatterWrap);
        }
    },

    enableAttractorPlugin: function ()
    {
        Plugin.register(MatterAttractors);
        Plugin.use(MatterLib, MatterAttractors);

        return this;
    },

    enableWrapPlugin: function ()
    {
        Plugin.register(MatterWrap);
        Plugin.use(MatterLib, MatterWrap);

        return this;
    }

});

module.exports = Matter;


/**
 * [description]
 *
 * @function Phaser.Math.Easing.Quintic.In
 * @since 3.0.0
 *
 * @param {number} v - [description]
 *
 * @return {number} [description]
 */
var In = function (v)
{
    return v * v * v * v * v;
};

module.exports = In;


var GameObject = require('../../GameObject');

var StaticTilemapLayerCanvasRenderer = function (renderer, gameObject, interpolationPercentage, camera)
{
    if (GameObject.RENDER_MASK !== gameObject.renderFlags || (gameObject.cameraFilter > 0 && (gameObject.cameraFilter & camera._id)))
    {
        return;
    }

    gameObject.cull(camera);

    var renderTiles = gameObject.culledTiles;
    var tileset = this.tileset;
    var frame = gameObject.frame;
    var ctx = renderer.gameContext;
    var tileCount = renderTiles.length;
    var image = frame.source.image;
    var tx = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
    var ty = gameObject.y - camera.scrollY * gameObject.scrollFactorY;

    ctx.save();
    ctx.translate(tx, ty);
    ctx.rotate(gameObject.rotation);
    ctx.scale(gameObject.scaleX, gameObject.scaleY);
    ctx.scale(gameObject.flipX ? -1 : 1, gameObject.flipY ? -1 : 1);
    ctx.globalAlpha = gameObject.alpha;

    for (var index = 0; index < tileCount; ++index)
    {
        var tile = renderTiles[index];

        var tileTexCoords = tileset.getTileTextureCoordinates(tile.index);
        if (tileTexCoords === null) { continue; }

        ctx.drawImage(
            image,
            tileTexCoords.x, tileTexCoords.y,
            tile.width, tile.height,
            tile.pixelX, tile.pixelY,
            tile.width, tile.height
        );
    }

    ctx.restore();
};

module.exports = StaticTilemapLayerCanvasRenderer;


/**
* Note that 'this' in all functions here refer to the owning object.
* For example the Group, Stage, Sprite, etc. because the render function
* here is mapped to the prototype for the game object.
*/
Phaser.Renderer.Canvas.GameObjects.Stage = {

    TYPES: [
        Phaser.Stage.prototype
    ],

    render: function (renderer, src)
    {
        if (src.visible === false || src.alpha === 0)
        {
            return;
        }

        // if (src._mask)
        // {
        //     renderer.pushMask(src._mask);
        // }

        for (var i = 0; i < src.children.list.length; i++)
        {
            var child = src.children.list[i];

            child.render(renderer, child);
        }

        // if (src._mask)
        // {
        //     renderer.popMask();
        // }

    }

};


var LightFragmentShader = function (maxLights)
{
    var frag = [
        '// Light Fragment Shader ',

        'precision mediump float;',

        'struct Light',
        '{',
        '    vec3 position;',
        '    vec3 color;',
        '    float attenuation;',
        '    float radius;',
        '};',

        'uniform vec4 uCamera; /* x, y, rotation, zoom */',
        'uniform vec2 uResolution;',
        'uniform sampler2D uMainTexture;',
        'uniform sampler2D uNormTexture;',
        'uniform vec3 uAmbientLightColor;',
        'uniform Light uLights[' + maxLights + '];',

        'varying vec2 v_tex_coord;',
        'varying vec3 v_color;',
        'varying float v_alpha;',

        'void main()',
        '{',
        '    vec3 finalColor = vec3(0.0, 0.0, 0.0);',
        '    vec4 spriteColor = texture2D(uMainTexture, v_tex_coord) * vec4(v_color, v_alpha);',
        '    vec3 spriteNormal = texture2D(uNormTexture, v_tex_coord).rgb;',
        '    vec3 normal = normalize(vec3(spriteNormal * 2.0 - 1.0));',
        '    vec2 res = vec2(min(uResolution.x, uResolution.y)) * uCamera.w;',

        '    for (int index = 0; index < ' + maxLights + '; ++index)',
        '    {',
        '        Light light = uLights[index];',
        '        vec3 lightDir = vec3((light.position.xy / res) - (gl_FragCoord.xy / res), light.position.z);',
        '        vec3 lightNormal = normalize(lightDir);',
        '        float distToSurf = length(lightDir) * uCamera.w;',
        '        float diffuseFactor = max(dot(normal, lightNormal), 0.0);',
        '        float radius = (light.radius / res.x * uCamera.w) * uCamera.w;',
        '        float attenuation = clamp(1.0 - distToSurf * distToSurf / (radius * radius), 0.0, 1.0);',
        '        vec3 diffuse = light.color * spriteColor.rgb * diffuseFactor;',
        '        finalColor += attenuation * diffuse;',
        '    }',

        '    vec4 color_output = vec4(uAmbientLightColor + finalColor, spriteColor.a);',
        '    gl_FragColor = vec4(color_output.rgb * color_output.a, color_output.a);',
        '}'
    ];

    return frag.join('\n');
};

module.exports = LightFragmentShader;


var Class = require('../../utils/Class');
var Components = require('../components');
var DataProxy = require('../components/DataProxy');
var TransformMatrix = require('../components/TransformMatrix');

var Container = new Class({

    // Mixins: [
    //     Components.Visible,
    //     SpriteRender
    // ],

    initialize:

    function Container (scene, x, y)
    {
        this.scene = scene;

        this.type = 'Container';

        this.name = '';

        //  Likely swap for a ProcessQueue to make it iteration safe
        this.list = [];

        this.active = true;

        this.data = new DataProxy(scene, this);

        this._depth = 0;

        this._transform = new TransformMatrix();

        this._x = x;
        this._y = y;
        this._scaleX = 1;
        this._scaleY = 1;
        this._rotation = 0;

        this._visible = true;
    },

    x: {

        get: function ()
        {
            return this._x;
        },

        set: function (value)
        {
            this._x = value;

            for (var i = 0; i < this.list.length; i++)
            {
                // this.list[i].depth = value;
            }
        }

    },

    y: {

        get: function ()
        {
            return this._y;
        },

        set: function (value)
        {
            this._y = value;

            for (var i = 0; i < this.list.length; i++)
            {
                // this.list[i].depth = value;
            }
        }

    },

    depth: {

        get: function ()
        {
            return this._depth;
        },

        set: function (value)
        {
            this._depth = value;

            for (var i = 0; i < this.list.length; i++)
            {
                this.list[i].depth = value;
            }
        }

    },

    setDepth: function (value)
    {
        if (value === undefined) { value = 0; }

        this.depth = value;

        return this;
    },

    visible: {

        get: function ()
        {
            return this._visible;
        },

        set: function (value)
        {
            if (value)
            {
                this._visible = true;
            }
            else
            {
                this._visible = false;
            }

            for (var i = 0; i < this.list.length; i++)
            {
                this.list[i].visible = value;
            }
        }

    },

    setVisible: function (value)
    {
        this.visible = value;

        return this;
    },

    add: function (child)
    {
        //  Don't allow containers to be added

        //  Is child already in this container?

        if (this.getIndex(child) === -1 && child.parent !== this)
        {
            //  No, good ...
            this.scene.sys.updateList.remove(child);

            if (child.parent)
            {
                child.parent.remove(child);
            }

            child.parent = this;

            this.list.push(child);

            // this.scene.sys.sortChildrenFlag = true;
        }

        return child;
    },

    remove: function (child)
    {
        var index = this.list.indexOf(child);

        if (index !== -1)
        {
            //  Not iteration safe - use ProcessQueue instead?
            this.list.splice(index, 1);

            child.parent = null;

            // this.scene.sys.sortChildrenFlag = true;
        }

        return child;
    },

    preUpdate: function (time, delta)
    {
        //  iterate children and call preUpdate on them, as they won't be part of the Scenes updateList
    },

    setActive: function (value)
    {
        this.active = value;

        return this;
    },

    setName: function (value)
    {
        this.name = value;

        return this;
    },

    setData: function (key, value)
    {
        this.data.set(key, value);

        return this;
    },

    getData: function (key)
    {
        return this.data.get(key);
    },

    destroy: function ()
    {
    }

});

module.exports = Container;


var tx = gameObject.x - camera.scrollX * gameObject.scrollFactorX;

var ty = gameObject.y - camera.scrollY * gameObject.scrollFactorY;

var image = tileset.image.getSourceImage();

var GameObject = require('../../GameObject');

var StaticTilemapLayerWebGLRenderer = function (renderer, src, interpolationPercentage, camera)
{
    if (GameObject.RENDER_MASK !== src.renderFlags || (src.cameraFilter > 0 && (src.cameraFilter & camera._id)))
    {
        return;
    }

    var gameObject = src;
    var frame = gameObject.frame;
    var gl = gameObject.gl;

    renderer.setRenderer(gameObject.tilemapRenderer, frame.texture.source[frame.sourceIndex].glTexture, gameObject.renderTarget);

    gameObject.tilemapRenderer.bind();
    gameObject.upload(camera);
    gameObject.vbo.bind();

    gl.drawArrays(gl.TRIANGLES, 0, gameObject.vertexCount);
};

module.exports = StaticTilemapLayerWebGLRenderer;


/**
* The `Matter.World` module contains methods for creating and manipulating the world composite.
* A `Matter.World` is a `Matter.Composite` body, which is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite`.
* A `Matter.World` has a few additional properties including `gravity` and `bounds`.
* It is important to use the functions in the `Matter.Composite` module to modify the world composite, rather than directly modifying its properties.
* There are also a few methods here that alias those in `Matter.Composite` for easier readability.
*
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).
*
* @class World
* @extends Composite
*/

var World = {};

module.exports = World;

var Composite = require('./Composite');
var Constraint = require('../constraint/Constraint');
var Common = require('../core/Common');

(function() {

    /**
     * Creates a new world composite. The options parameter is an object that specifies any properties you wish to override the defaults.
     * See the properties section below for detailed information on what you can pass via the `options` object.
     * @method create
     * @constructor
     * @param {} options
     * @return {world} A new world
     */
    World.create = function(options) {
        var composite = Composite.create();

        var defaults = {
            label: 'World',
            gravity: {
                x: 0,
                y: 1,
                scale: 0.001
            },
            bounds: {
                min: { x: -Infinity, y: -Infinity },
                max: { x: Infinity, y: Infinity }
            }
        };

        return Common.extend(composite, defaults, options);
    };

    /*
    *
    *  Properties Documentation
    *
    */

    /**
     * The gravity to apply on the world.
     *
     * @property gravity
     * @type object
     */

    /**
     * The gravity x component.
     *
     * @property gravity.x
     * @type object
     * @default 0
     */

    /**
     * The gravity y component.
     *
     * @property gravity.y
     * @type object
     * @default 1
     */

    /**
     * The gravity scale factor.
     *
     * @property gravity.scale
     * @type object
     * @default 0.001
     */

    /**
     * A `Bounds` object that defines the world bounds for collision detection.
     *
     * @property bounds
     * @type bounds
     * @default { min: { x: -Infinity, y: -Infinity }, max: { x: Infinity, y: Infinity } }
     */

    // World is a Composite body
    // see src/module/Outro.js for these aliases:

    /**
     * An alias for Composite.clear
     * @method clear
     * @param {world} world
     * @param {boolean} keepStatic
     */

    /**
     * An alias for Composite.add
     * @method addComposite
     * @param {world} world
     * @param {composite} composite
     * @return {world} The original world with the objects from composite added
     */

     /**
      * An alias for Composite.addBody
      * @method addBody
      * @param {world} world
      * @param {body} body
      * @return {world} The original world with the body added
      */

     /**
      * An alias for Composite.addConstraint
      * @method addConstraint
      * @param {world} world
      * @param {constraint} constraint
      * @return {world} The original world with the constraint added
      */

})();


var Class = require('../../../../utils/Class');
var CONST = require('./const');
var DataBuffer16 = require('../../utils/DataBuffer16');
var DataBuffer32 = require('../../utils/DataBuffer32');
var PHASER_CONST = require('../../../../const');
var TexturedAndNormalizedTintedShader = require('../../shaders/TexturedAndNormalizedTintedShader');
var TransformMatrix = require('../../../../gameobjects/components/TransformMatrix');

var SpriteBatch = new Class({

    initialize:

    function SpriteBatch (game, gl, manager)
    {
        this.game = game;
        this.type = PHASER_CONST.WEBGL;
        this.view = game.canvas;
        this.resolution = game.config.resolution;
        this.width = game.config.width * game.config.resolution;
        this.height = game.config.height * game.config.resolution;
        this.glContext = gl;
        this.maxSprites = null;
        this.shader = null;
        this.vertexBufferObject = null;
        this.indexBufferObject = null;
        this.indexBufferObjectForMesh = null;
        this.vertexDataBuffer = null;
        this.indexDataBuffer = null;
        this.elementCount = 0;
        this.currentTexture2D = null;
        this.viewMatrixLocation = null;
        this.tempMatrix = new TransformMatrix();
        this.usingRenderTexture = false;

        //   All of these settings will be able to be controlled via the Game Config
        this.config = {
            clearBeforeRender: true,
            transparent: false,
            autoResize: false,
            preserveDrawingBuffer: false,

            WebGLContextOptions: {
                alpha: true,
                antialias: true,
                premultipliedAlpha: true,
                stencil: true,
                preserveDrawingBuffer: false
            }
        };

        this.manager = manager;
        this.dirty = false;
        this.drawIndexed = true;
        this.lastDrawIndexed = true;
        this.lastDrawingMesh = false;
        this.drawingMesh = false;
        this.vertexCount = 0;

        this.init(this.glContext);
    },

    init: function (gl)
    {
        var vertexDataBuffer = new DataBuffer32(CONST.VERTEX_SIZE * CONST.SPRITE_VERTEX_COUNT * CONST.MAX_SPRITES);
        var indexDataBuffer = new DataBuffer16(CONST.INDEX_SIZE * CONST.SPRITE_INDEX_COUNT * CONST.MAX_SPRITES);
        var shader = this.manager.resourceManager.createShader('TexturedAndNormalizedTintedShader', TexturedAndNormalizedTintedShader);
        var indexBufferObject = this.manager.resourceManager.createBuffer(gl.ELEMENT_ARRAY_BUFFER, indexDataBuffer.getByteCapacity(), gl.STATIC_DRAW);
        var indexBufferObjectForMesh = this.manager.resourceManager.createBuffer(gl.ELEMENT_ARRAY_BUFFER, indexDataBuffer.getByteCapacity(), gl.STREAM_DRAW);
        var vertexBufferObject = this.manager.resourceManager.createBuffer(gl.ARRAY_BUFFER, vertexDataBuffer.getByteCapacity(), gl.STREAM_DRAW);
        var viewMatrixLocation = shader.getUniformLocation('u_view_matrix');
        var indexBuffer = indexDataBuffer.uintView;
        var max = CONST.MAX_SPRITES * CONST.SPRITE_INDEX_COUNT;

        this.vertexDataBuffer = vertexDataBuffer;
        this.indexDataBuffer = indexDataBuffer;
        this.shader = shader;
        this.indexBufferObject = indexBufferObject;
        this.indexBufferObjectForMesh = indexBufferObjectForMesh;
        this.vertexBufferObject = vertexBufferObject;
        this.viewMatrixLocation = viewMatrixLocation;

        vertexBufferObject.addAttribute(shader.getAttribLocation('a_position'), 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 0);
        vertexBufferObject.addAttribute(shader.getAttribLocation('a_tex_coord'), 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 8);
        vertexBufferObject.addAttribute(shader.getAttribLocation('a_color'), 3, gl.UNSIGNED_BYTE, true, CONST.VERTEX_SIZE, 16);
        vertexBufferObject.addAttribute(shader.getAttribLocation('a_alpha'), 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 20);

        // Populate the index buffer only once
        for (var indexA = 0, indexB = 0; indexA < max; indexA += CONST.SPRITE_INDEX_COUNT, indexB += CONST.SPRITE_VERTEX_COUNT)
        {
            indexBuffer[indexA + 0] = indexB + 0;
            indexBuffer[indexA + 1] = indexB + 1;
            indexBuffer[indexA + 2] = indexB + 2;
            indexBuffer[indexA + 3] = indexB + 0;
            indexBuffer[indexA + 4] = indexB + 2;
            indexBuffer[indexA + 5] = indexB + 3;
        }

        indexBufferObject.updateResource(indexBuffer, 0);

        this.resize(this.width, this.height, this.game.config.resolution);
    },

    shouldFlush: function ()
    {
        if (this.drawIndexed !== this.lastDrawIndexed || this.lastDrawingMesh !== this.drawingMesh || this.isFull())
        {
            this.lastDrawIndexed = this.drawIndexed;
            this.lastDrawingMesh = this.drawingMesh;
            return true;
        }

        return false;
    },

    isFull: function ()
    {
        return (this.vertexDataBuffer.getByteLength() >= this.vertexDataBuffer.getByteCapacity());
    },

    bind: function (shader)
    {
        if (!shader)
        {
            this.shader.bind();
        }
        else
        {
            shader.bind();
            this.resize(this.width, this.height, this.game.config.resolution, shader);
        }

        this.vertexBufferObject.bind();
    },

    flush: function (shader, renderTarget)
    {
        var gl = this.glContext;
        var vertexDataBuffer = this.vertexDataBuffer;

        if (this.elementCount === 0 && this.vertexCount === 0)
        {
            return;
        }

        if (renderTarget)
        {
            gl.bindFramebuffer(gl.FRAMEBUFFER, renderTarget.framebufferObject);
        }

        this.bind(shader);
        this.vertexBufferObject.updateResource(vertexDataBuffer.getUsedBufferAsFloat(), 0);

        if (this.drawIndexed)
        {
            if (this.drawingMesh)
            {
                this.indexBufferObjectForMesh.bind();
                this.indexBufferObjectForMesh.updateResource(this.indexDataBuffer.buffer, 0);
            }
            else
            {
                this.indexBufferObject.bind();
            }

            gl.drawElements(gl.TRIANGLES, this.elementCount, gl.UNSIGNED_SHORT, 0);
        }
        else
        {
            gl.drawArrays(gl.TRIANGLES, 0, this.vertexCount);
        }

        vertexDataBuffer.clear();
        this.elementCount = 0;
        this.vertexCount = 0;

        if (renderTarget)
        {
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        }
    },

    resize: function (width, height, resolution, shader)
    {
        var activeShader = (shader) ? shader : this.shader;
        var location = (activeShader === this.shader) ? this.viewMatrixLocation : activeShader.getUniformLocation('u_view_matrix');

        this.width = width * resolution;
        this.height = height * resolution;
        this.setProjectionMatrix(activeShader, location);
    },

    setProjectionMatrix: function (shader, location)
    {
        shader.setConstantMatrix4x4(
            location,
            new Float32Array([
                2 / this.width, 0, 0, 0,
                0, -2 / this.height, 0, 0,
                0, 0, 1, 1,
                -1, 1, 0, 0
            ])
        );
    },

    destroy: function ()
    {
        this.manager.resourceManager.deleteShader(this.shader);
        this.manager.resourceManager.deleteBuffer(this.indexBufferObject);
        this.manager.resourceManager.deleteBuffer(this.vertexBufferObject);

        this.shader = null;
        this.indexBufferObject = null;
        this.vertexBufferObject = null;
    },

    addMeshIndexed: function (gameObject, camera)
    {
        var tempMatrix = this.tempMatrix;
        var frame = gameObject.frame;
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferObjectF32 = vertexDataBuffer.floatView;
        var vertexBufferObjectU32 = vertexDataBuffer.uintView;
        var vertexOffset = 0;
        var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
        var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
        var scaleX = gameObject.scaleX;
        var scaleY = gameObject.scaleY;
        var rotation = -gameObject.rotation;
        var tempMatrixMatrix = tempMatrix.matrix;
        var cameraMatrix = camera.matrix.matrix;
        var mva, mvb, mvc, mvd, mve, mvf;
        var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
        var vertices = gameObject.vertices;
        var uv = gameObject.uv;
        var length = vertices.length;
        var totalVertices = (length / 2)|0;
        var indexBuffer = this.indexDataBuffer.uintView;
        var indices = gameObject.indices;
        var colors = gameObject.colors;
        var alphas = gameObject.alphas;
        var indexLength = indices.length;
        var indexOffset = 0;

        tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);

        sra = tempMatrixMatrix[0];
        srb = tempMatrixMatrix[1];
        src = tempMatrixMatrix[2];
        srd = tempMatrixMatrix[3];
        sre = tempMatrixMatrix[4];
        srf = tempMatrixMatrix[5];

        cma = cameraMatrix[0];
        cmb = cameraMatrix[1];
        cmc = cameraMatrix[2];
        cmd = cameraMatrix[3];
        cme = cameraMatrix[4];
        cmf = cameraMatrix[5];

        mva = sra * cma + srb * cmc;
        mvb = sra * cmb + srb * cmd;
        mvc = src * cma + srd * cmc;
        mvd = src * cmb + srd * cmd;
        mve = sre * cma + srf * cmc + cme;
        mvf = sre * cmb + srf * cmd + cmf;

        this.manager.setRenderer(this, frame.texture.source[frame.sourceIndex].glTexture, gameObject.renderTarget);
        indexOffset = this.vertexCount;
        this.drawIndexed = true;
        this.drawingMesh = true;
        this.vertexCount += totalVertices;

        vertexOffset = vertexDataBuffer.allocate(totalVertices * 6);

        var index;
        var index0;

        for (index = 0, index0 = 0; index < length; index += 2)
        {
            var x = vertices[index + 0];
            var y = vertices[index + 1];
            var tx = x * mva + y * mvc + mve;
            var ty = x * mvb + y * mvd + mvf;
            vertexBufferObjectF32[vertexOffset++] = tx;
            vertexBufferObjectF32[vertexOffset++] = ty;
            vertexBufferObjectF32[vertexOffset++] = uv[index + 0];
            vertexBufferObjectF32[vertexOffset++] = uv[index + 1];
            vertexBufferObjectU32[vertexOffset++] = colors[index0];
            vertexBufferObjectF32[vertexOffset++] = alphas[index0];
            index0 += 1;
        }

        var elementCount = this.elementCount;

        for (index = 0; index < indexLength; ++index)
        {
            indexBuffer[elementCount + index] = indexOffset + indices[index];
        }

        this.elementCount += indexLength;
    },

    addMesh: function (gameObject, camera)
    {
        var tempMatrix = this.tempMatrix;
        var frame = gameObject.frame;
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferObjectF32 = vertexDataBuffer.floatView;
        var vertexBufferObjectU32 = vertexDataBuffer.uintView;
        var vertexOffset = 0;
        var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
        var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
        var scaleX = gameObject.scaleX;
        var scaleY = gameObject.scaleY;
        var rotation = -gameObject.rotation;
        var tempMatrixMatrix = tempMatrix.matrix;
        var cameraMatrix = camera.matrix.matrix;
        var mva, mvb, mvc, mvd, mve, mvf;
        var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
        var vertices = gameObject.vertices;
        var uv = gameObject.uv;
        var colors = gameObject.colors;
        var alphas = gameObject.alphas;
        var length = vertices.length;
        var totalVertices = (length / 2)|0;

        tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);

        sra = tempMatrixMatrix[0];
        srb = tempMatrixMatrix[1];
        src = tempMatrixMatrix[2];
        srd = tempMatrixMatrix[3];
        sre = tempMatrixMatrix[4];
        srf = tempMatrixMatrix[5];

        cma = cameraMatrix[0];
        cmb = cameraMatrix[1];
        cmc = cameraMatrix[2];
        cmd = cameraMatrix[3];
        cme = cameraMatrix[4];
        cmf = cameraMatrix[5];

        mva = sra * cma + srb * cmc;
        mvb = sra * cmb + srb * cmd;
        mvc = src * cma + srd * cmc;
        mvd = src * cmb + srd * cmd;
        mve = sre * cma + srf * cmc + cme;
        mvf = sre * cmb + srf * cmd + cmf;

        this.manager.setRenderer(this, frame.texture.source[frame.sourceIndex].glTexture, gameObject.renderTarget);
        this.drawIndexed = false;
        this.drawingMesh = true;
        this.vertexCount += totalVertices;

        vertexOffset = vertexDataBuffer.allocate(totalVertices * 6);

        for (var index = 0, index0 = 0; index < length; index += 2)
        {
            var x = vertices[index + 0];
            var y = vertices[index + 1];
            var tx = x * mva + y * mvc + mve;
            var ty = x * mvb + y * mvd + mvf;
            vertexBufferObjectF32[vertexOffset++] = tx;
            vertexBufferObjectF32[vertexOffset++] = ty;
            vertexBufferObjectF32[vertexOffset++] = uv[index + 0];
            vertexBufferObjectF32[vertexOffset++] = uv[index + 1];
            vertexBufferObjectU32[vertexOffset++] = colors[index0];
            vertexBufferObjectF32[vertexOffset++] = alphas[index0];
            index0 += 1;
        }
    },

    addRenderPassRect: function (x, y, width, height, scrollFactorX, scrollFactorY, camera, passShader, passRenderTarget)
    {
        if (this.vertexCount > 0)
        {
            this.flush();
        }

        this.drawIndexed = false;
        this.vertexCount = 6;
        this.vertexDataBuffer.allocate(36);

        y += height;

        var gl = this.glContext;
        var scrollX = camera.scrollX * scrollFactorX;
        var scrollY = camera.scrollY * scrollFactorY;
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferObjectF32 = vertexDataBuffer.floatView;
        var vertexBufferObjectU32 = vertexDataBuffer.uintView;
        var cameraMatrix = camera.matrix.matrix;
        var xw = x + width;
        var yh = y + height;
        var mva = cameraMatrix[0];
        var mvb = cameraMatrix[1];
        var mvc = cameraMatrix[2];
        var mvd = cameraMatrix[3];
        var mve = cameraMatrix[4];
        var mvf = cameraMatrix[5];
        var tx0 = (x * mva + y * mvc + mve) - scrollX;
        var ty0 = (x * mvb + y * mvd + mvf) - scrollY;
        var tx1 = (x * mva + yh * mvc + mve) - scrollX;
        var ty1 = (x * mvb + yh * mvd + mvf) - scrollY;
        var tx2 = (xw * mva + yh * mvc + mve) - scrollX;
        var ty2 = (xw * mvb + yh * mvd + mvf) - scrollY;
        var tx3 = (xw * mva + y * mvc + mve) - scrollX;
        var ty3 = (xw * mvb + y * mvd + mvf) - scrollY;
        var u0 = 0;
        var v0 = 1;
        var u1 = 1;
        var v1 = 0;

        //  Top Left
        vertexBufferObjectF32[0] = tx0;
        vertexBufferObjectF32[1] = ty0;
        vertexBufferObjectF32[2] = u0;
        vertexBufferObjectF32[3] = v0;
        vertexBufferObjectU32[4] = 0xffffff;
        vertexBufferObjectF32[5] = 1.0;

        //  Bottom Left
        vertexBufferObjectF32[6] = tx1;
        vertexBufferObjectF32[7] = ty1;
        vertexBufferObjectF32[8] = u0;
        vertexBufferObjectF32[9] = v1;
        vertexBufferObjectU32[10] = 0xffffff;
        vertexBufferObjectF32[11] = 1.0;

        //  Bottom Right
        vertexBufferObjectF32[12] = tx2;
        vertexBufferObjectF32[13] = ty2;
        vertexBufferObjectF32[14] = u1;
        vertexBufferObjectF32[15] = v1;
        vertexBufferObjectU32[16] = 0xffffff;
        vertexBufferObjectF32[17] = 1.0;

        //  Top Left
        vertexBufferObjectF32[18] = tx0;
        vertexBufferObjectF32[19] = ty0;
        vertexBufferObjectF32[20] = u0;
        vertexBufferObjectF32[21] = v0;
        vertexBufferObjectU32[22] = 0xffffff;
        vertexBufferObjectF32[23] = 1.0;

        //  Bottom Right
        vertexBufferObjectF32[24] = tx2;
        vertexBufferObjectF32[25] = ty2;
        vertexBufferObjectF32[26] = u1;
        vertexBufferObjectF32[27] = v1;
        vertexBufferObjectU32[28] = 0xffffff;
        vertexBufferObjectF32[29] = 1.0;

        //  Top Right
        vertexBufferObjectF32[30] = tx3;
        vertexBufferObjectF32[31] = ty3;
        vertexBufferObjectF32[32] = u1;
        vertexBufferObjectF32[33] = v0;
        vertexBufferObjectU32[34] = 0xffffff;
        vertexBufferObjectF32[35] = 1.0;

        //gl.viewport(0, 0, subWidth, subHeight);
        this.flush(passShader, passRenderTarget);
    },

    addTileTextureRect: function (texture, x, y, width, height, alpha, tint, scrollFactorX, scrollFactorY, textureWidth, textureHeight, rectX, rectY, rectW, rectH, camera, renderTarget, flipX, flipY)
    {
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferObjectF32 = vertexDataBuffer.floatView;
        var vertexBufferObjectU32 = vertexDataBuffer.uintView;
        var vertexOffset = 0;
        var xw = x + width;
        var yh = y + height;
        var cameraMatrix = camera.matrix.matrix;
        var scrollX = camera.scrollX * scrollFactorX;
        var scrollY = camera.scrollY * scrollFactorY;
        var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;

        flipX = flipX ? flipX : false;
        flipY = flipY ? flipY : false;

        rectW = rectW * (flipX ? -1 : 1);
        rectH = rectH * (flipY ? -1 : 1);
        rectX = flipX ? -rectW : rectX;
        rectY = flipY ? -rectH : rectY;

        // Inset UV coordinates by 0.5px to prevent tile bleeding
        var u0 = (rectX + 0.5) / textureWidth;
        var v0 = (rectY + 0.5) / textureHeight;
        var u1 = (rectX - 0.5 + rectW) / textureWidth;
        var v1 = (rectY - 0.5 + rectH) / textureHeight;

        mva = cameraMatrix[0];
        mvb = cameraMatrix[1];
        mvc = cameraMatrix[2];
        mvd = cameraMatrix[3];
        mve = cameraMatrix[4];
        mvf = cameraMatrix[5];

        tx0 = (x * mva + y * mvc + mve) - scrollX;
        ty0 = (x * mvb + y * mvd + mvf) - scrollY;
        tx1 = (x * mva + yh * mvc + mve) - scrollX;
        ty1 = (x * mvb + yh * mvd + mvf) - scrollY;
        tx2 = (xw * mva + yh * mvc + mve) - scrollX;
        ty2 = (xw * mvb + yh * mvd + mvf) - scrollY;
        tx3 = (xw * mva + y * mvc + mve) - scrollX;
        ty3 = (xw * mvb + y * mvd + mvf) - scrollY;

        this.manager.setRenderer(this, texture, renderTarget);
        this.drawIndexed = true;
        this.drawingMesh = false;
        this.elementCount += 6;
        vertexOffset = vertexDataBuffer.allocate(24);

        vertexBufferObjectF32[vertexOffset++] = tx0;
        vertexBufferObjectF32[vertexOffset++] = ty0;
        vertexBufferObjectF32[vertexOffset++] = u0;
        vertexBufferObjectF32[vertexOffset++] = v0;
        vertexBufferObjectU32[vertexOffset++] = tint;
        vertexBufferObjectF32[vertexOffset++] = alpha;

        vertexBufferObjectF32[vertexOffset++] = tx1;
        vertexBufferObjectF32[vertexOffset++] = ty1;
        vertexBufferObjectF32[vertexOffset++] = u0;
        vertexBufferObjectF32[vertexOffset++] = v1;
        vertexBufferObjectU32[vertexOffset++] = tint;
        vertexBufferObjectF32[vertexOffset++] = alpha;

        vertexBufferObjectF32[vertexOffset++] = tx2;
        vertexBufferObjectF32[vertexOffset++] = ty2;
        vertexBufferObjectF32[vertexOffset++] = u1;
        vertexBufferObjectF32[vertexOffset++] = v1;
        vertexBufferObjectU32[vertexOffset++] = tint;
        vertexBufferObjectF32[vertexOffset++] = alpha;

        vertexBufferObjectF32[vertexOffset++] = tx3;
        vertexBufferObjectF32[vertexOffset++] = ty3;
        vertexBufferObjectF32[vertexOffset++] = u1;
        vertexBufferObjectF32[vertexOffset++] = v0;
        vertexBufferObjectU32[vertexOffset++] = tint;
        vertexBufferObjectF32[vertexOffset++] = alpha;
    },

    addSpriteTextureRect: function (gameObject, camera, texture, rectX, rectY, rectWidth, rectHeight, textureWidth, textureHeight)
    {
        var tempMatrix = this.tempMatrix;
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferObjectF32 = vertexDataBuffer.floatView;
        var vertexBufferObjectU32 = vertexDataBuffer.uintView;
        var vertexOffset = 0;
        var forceFlipY = (texture.isRenderTexture ? true : false);
        var flipX = gameObject.flipX;
        var flipY = gameObject.flipY ^ forceFlipY;
        var width = rectWidth * (flipX ? -1 : 1);
        var height = rectHeight * (flipY ? -1 : 1);
        var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
        var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
        var scaleX = gameObject.scaleX;
        var scaleY = gameObject.scaleY;
        var rotation = -gameObject.rotation;
        var tempMatrixMatrix = tempMatrix.matrix;
        var x = -gameObject.displayOriginX + ((rectWidth) * (flipX ? 1 : 0.0));
        var y = -gameObject.displayOriginY + ((rectHeight) * (flipY ? 1 : 0.0));
        var xw = x + rectWidth;
        var yh = y + rectHeight;
        var cameraMatrix = camera.matrix.matrix;
        var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;
        var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
        var alphaTL = gameObject._alphaTL;
        var alphaTR = gameObject._alphaTR;
        var alphaBL = gameObject._alphaBL;
        var alphaBR = gameObject._alphaBR;
        var tintTL = gameObject._tintTL;
        var tintTR = gameObject._tintTR;
        var tintBL = gameObject._tintBL;
        var tintBR = gameObject._tintBR;
        var u0 = 0; // rectX / textureWidth;
        var v0 = 0; // rectY / textureHeight;
        var u1 = 1; // u0 + (rectWidth / textureWidth);
        var v1 = 1; // v0 + (rectHeight / textureHeight);

        tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);

        sra = tempMatrixMatrix[0];
        srb = tempMatrixMatrix[1];
        src = tempMatrixMatrix[2];
        srd = tempMatrixMatrix[3];
        sre = tempMatrixMatrix[4];
        srf = tempMatrixMatrix[5];

        cma = cameraMatrix[0];
        cmb = cameraMatrix[1];
        cmc = cameraMatrix[2];
        cmd = cameraMatrix[3];
        cme = cameraMatrix[4];
        cmf = cameraMatrix[5];

        mva = sra * cma + srb * cmc;
        mvb = sra * cmb + srb * cmd;
        mvc = src * cma + srd * cmc;
        mvd = src * cmb + srd * cmd;
        mve = sre * cma + srf * cmc + cme;
        mvf = sre * cmb + srf * cmd + cmf;

        tx0 = x * mva + y * mvc + mve;
        ty0 = x * mvb + y * mvd + mvf;
        tx1 = x * mva + yh * mvc + mve;
        ty1 = x * mvb + yh * mvd + mvf;
        tx2 = xw * mva + yh * mvc + mve;
        ty2 = xw * mvb + yh * mvd + mvf;
        tx3 = xw * mva + y * mvc + mve;
        ty3 = xw * mvb + y * mvd + mvf;

        this.manager.setRenderer(this, texture, gameObject.renderTarget);
        this.drawIndexed = true;
        this.drawingMesh = false;
        vertexOffset = vertexDataBuffer.allocate(24);
        this.elementCount += 6;

        //  Top Left
        vertexBufferObjectF32[vertexOffset++] = tx0;
        vertexBufferObjectF32[vertexOffset++] = ty0;
        vertexBufferObjectF32[vertexOffset++] = u0;
        vertexBufferObjectF32[vertexOffset++] = v0;
        vertexBufferObjectU32[vertexOffset++] = tintTL;
        vertexBufferObjectF32[vertexOffset++] = alphaTL;

        //  Bottom Left
        vertexBufferObjectF32[vertexOffset++] = tx1;
        vertexBufferObjectF32[vertexOffset++] = ty1;
        vertexBufferObjectF32[vertexOffset++] = u0;
        vertexBufferObjectF32[vertexOffset++] = v1;
        vertexBufferObjectU32[vertexOffset++] = tintBL;
        vertexBufferObjectF32[vertexOffset++] = alphaBL;

        //  Bottom Right
        vertexBufferObjectF32[vertexOffset++] = tx2;
        vertexBufferObjectF32[vertexOffset++] = ty2;
        vertexBufferObjectF32[vertexOffset++] = u1;
        vertexBufferObjectF32[vertexOffset++] = v1;
        vertexBufferObjectU32[vertexOffset++] = tintBR;
        vertexBufferObjectF32[vertexOffset++] = alphaBR;

        //  Top Right
        vertexBufferObjectF32[vertexOffset++] = tx3;
        vertexBufferObjectF32[vertexOffset++] = ty3;
        vertexBufferObjectF32[vertexOffset++] = u1;
        vertexBufferObjectF32[vertexOffset++] = v0;
        vertexBufferObjectU32[vertexOffset++] = tintTR;
        vertexBufferObjectF32[vertexOffset++] = alphaTR;
    },

    addSpriteTexture: function (gameObject, camera, texture, textureWidth, textureHeight)
    {
        var tempMatrix = this.tempMatrix;
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferObjectF32 = vertexDataBuffer.floatView;
        var vertexBufferObjectU32 = vertexDataBuffer.uintView;
        var vertexOffset = 0;
        var forceFlipY = (texture.isRenderTexture ? true : false);
        var flipX = gameObject.flipX;
        var flipY = gameObject.flipY ^ forceFlipY;
        var width = textureWidth * (flipX ? -1 : 1);
        var height = textureHeight * (flipY ? -1 : 1);
        var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
        var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
        var scaleX = gameObject.scaleX;
        var scaleY = gameObject.scaleY;
        var rotation = -gameObject.rotation;
        var tempMatrixMatrix = tempMatrix.matrix;
        var x = -gameObject.displayOriginX + ((textureWidth) * (flipX ? 1 : 0.0));
        var y = -gameObject.displayOriginY + ((textureHeight) * (flipY ? 1 : 0.0));
        var xw = x + width;
        var yh = y + height;
        var cameraMatrix = camera.matrix.matrix;
        var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;
        var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
        var alphaTL = gameObject._alphaTL;
        var alphaTR = gameObject._alphaTR;
        var alphaBL = gameObject._alphaBL;
        var alphaBR = gameObject._alphaBR;
        var tintTL = gameObject._tintTL;
        var tintTR = gameObject._tintTR;
        var tintBL = gameObject._tintBL;
        var tintBR = gameObject._tintBR;

        tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);

        sra = tempMatrixMatrix[0];
        srb = tempMatrixMatrix[1];
        src = tempMatrixMatrix[2];
        srd = tempMatrixMatrix[3];
        sre = tempMatrixMatrix[4];
        srf = tempMatrixMatrix[5];

        cma = cameraMatrix[0];
        cmb = cameraMatrix[1];
        cmc = cameraMatrix[2];
        cmd = cameraMatrix[3];
        cme = cameraMatrix[4];
        cmf = cameraMatrix[5];

        mva = sra * cma + srb * cmc;
        mvb = sra * cmb + srb * cmd;
        mvc = src * cma + srd * cmc;
        mvd = src * cmb + srd * cmd;
        mve = sre * cma + srf * cmc + cme;
        mvf = sre * cmb + srf * cmd + cmf;

        tx0 = x * mva + y * mvc + mve;
        ty0 = x * mvb + y * mvd + mvf;
        tx1 = x * mva + yh * mvc + mve;
        ty1 = x * mvb + yh * mvd + mvf;
        tx2 = xw * mva + yh * mvc + mve;
        ty2 = xw * mvb + yh * mvd + mvf;
        tx3 = xw * mva + y * mvc + mve;
        ty3 = xw * mvb + y * mvd + mvf;

        this.manager.setRenderer(this, texture, gameObject.renderTarget);
        this.drawIndexed = true;
        this.drawingMesh = false;
        vertexOffset = vertexDataBuffer.allocate(24);
        this.elementCount += 6;

        //  Top Left
        vertexBufferObjectF32[vertexOffset++] = tx0;
        vertexBufferObjectF32[vertexOffset++] = ty0;
        vertexBufferObjectF32[vertexOffset++] = 0;
        vertexBufferObjectF32[vertexOffset++] = 0;
        vertexBufferObjectU32[vertexOffset++] = tintTL;
        vertexBufferObjectF32[vertexOffset++] = alphaTL;

        //  Bottom Left
        vertexBufferObjectF32[vertexOffset++] = tx1;
        vertexBufferObjectF32[vertexOffset++] = ty1;
        vertexBufferObjectF32[vertexOffset++] = 0;
        vertexBufferObjectF32[vertexOffset++] = 1;
        vertexBufferObjectU32[vertexOffset++] = tintBL;
        vertexBufferObjectF32[vertexOffset++] = alphaBL;

        //  Bottom Right
        vertexBufferObjectF32[vertexOffset++] = tx2;
        vertexBufferObjectF32[vertexOffset++] = ty2;
        vertexBufferObjectF32[vertexOffset++] = 1;
        vertexBufferObjectF32[vertexOffset++] = 1;
        vertexBufferObjectU32[vertexOffset++] = tintBR;
        vertexBufferObjectF32[vertexOffset++] = alphaBR;

        //  Top Right
        vertexBufferObjectF32[vertexOffset++] = tx3;
        vertexBufferObjectF32[vertexOffset++] = ty3;
        vertexBufferObjectF32[vertexOffset++] = 1;
        vertexBufferObjectF32[vertexOffset++] = 0;
        vertexBufferObjectU32[vertexOffset++] = tintTR;
        vertexBufferObjectF32[vertexOffset++] = alphaTR;
    },

    addSprite: function (gameObject, camera)
    {
        var tempMatrix = this.tempMatrix;
        var frame = gameObject.frame;
        var forceFlipY = (frame.texture.source[frame.sourceIndex].glTexture.isRenderTexture ? true : false);
        var flipX = gameObject.flipX;
        var flipY = gameObject.flipY ^ forceFlipY;
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferObjectF32 = vertexDataBuffer.floatView;
        var vertexBufferObjectU32 = vertexDataBuffer.uintView;
        var vertexOffset = 0;
        var uvs = frame.uvs;
        var width = frame.width * (flipX ? -1 : 1);
        var height = frame.height * (flipY ? -1 : 1);
        var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
        var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
        var scaleX = gameObject.scaleX;
        var scaleY = gameObject.scaleY;
        var rotation = -gameObject.rotation;
        var tempMatrixMatrix = tempMatrix.matrix;
        var x = -gameObject.displayOriginX + frame.x + ((frame.width) * (flipX ? 1 : 0.0));
        var y = -gameObject.displayOriginY + frame.y + ((frame.height) * (flipY ? 1 : 0.0));
        var xw = x + width;
        var yh = y + height;
        var cameraMatrix = camera.matrix.matrix;
        var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;
        var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
        var alphaTL = gameObject._alphaTL;
        var alphaTR = gameObject._alphaTR;
        var alphaBL = gameObject._alphaBL;
        var alphaBR = gameObject._alphaBR;
        var tintTL = gameObject._tintTL;
        var tintTR = gameObject._tintTR;
        var tintBL = gameObject._tintBL;
        var tintBR = gameObject._tintBR;

        tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);

        sra = tempMatrixMatrix[0];
        srb = tempMatrixMatrix[1];
        src = tempMatrixMatrix[2];
        srd = tempMatrixMatrix[3];
        sre = tempMatrixMatrix[4];
        srf = tempMatrixMatrix[5];

        cma = cameraMatrix[0];
        cmb = cameraMatrix[1];
        cmc = cameraMatrix[2];
        cmd = cameraMatrix[3];
        cme = cameraMatrix[4];
        cmf = cameraMatrix[5];

        mva = sra * cma + srb * cmc;
        mvb = sra * cmb + srb * cmd;
        mvc = src * cma + srd * cmc;
        mvd = src * cmb + srd * cmd;
        mve = sre * cma + srf * cmc + cme;
        mvf = sre * cmb + srf * cmd + cmf;

        tx0 = x * mva + y * mvc + mve;
        ty0 = x * mvb + y * mvd + mvf;
        tx1 = x * mva + yh * mvc + mve;
        ty1 = x * mvb + yh * mvd + mvf;
        tx2 = xw * mva + yh * mvc + mve;
        ty2 = xw * mvb + yh * mvd + mvf;
        tx3 = xw * mva + y * mvc + mve;
        ty3 = xw * mvb + y * mvd + mvf;

        this.manager.setRenderer(this, frame.texture.source[frame.sourceIndex].glTexture, gameObject.renderTarget);
        this.drawIndexed = true;
        this.drawingMesh = false;
        vertexOffset = vertexDataBuffer.allocate(24);
        this.elementCount += 6;

        //  Top Left
        vertexBufferObjectF32[vertexOffset++] = tx0;
        vertexBufferObjectF32[vertexOffset++] = ty0;
        vertexBufferObjectF32[vertexOffset++] = uvs.x0;
        vertexBufferObjectF32[vertexOffset++] = uvs.y0;
        vertexBufferObjectU32[vertexOffset++] = tintTL;
        vertexBufferObjectF32[vertexOffset++] = alphaTL;

        //  Bottom Left
        vertexBufferObjectF32[vertexOffset++] = tx1;
        vertexBufferObjectF32[vertexOffset++] = ty1;
        vertexBufferObjectF32[vertexOffset++] = uvs.x1;
        vertexBufferObjectF32[vertexOffset++] = uvs.y1;
        vertexBufferObjectU32[vertexOffset++] = tintBL;
        vertexBufferObjectF32[vertexOffset++] = alphaBL;

        //  Bottom Right
        vertexBufferObjectF32[vertexOffset++] = tx2;
        vertexBufferObjectF32[vertexOffset++] = ty2;
        vertexBufferObjectF32[vertexOffset++] = uvs.x2;
        vertexBufferObjectF32[vertexOffset++] = uvs.y2;
        vertexBufferObjectU32[vertexOffset++] = tintBR;
        vertexBufferObjectF32[vertexOffset++] = alphaBR;

        //  Top Right
        vertexBufferObjectF32[vertexOffset++] = tx3;
        vertexBufferObjectF32[vertexOffset++] = ty3;
        vertexBufferObjectF32[vertexOffset++] = uvs.x3;
        vertexBufferObjectF32[vertexOffset++] = uvs.y3;
        vertexBufferObjectU32[vertexOffset++] = tintTR;
        vertexBufferObjectF32[vertexOffset++] = alphaTR;
    }

});

module.exports = SpriteBatch;


// Note: direct port from v2

/**
* An Image Collection is a special tileset containing mulitple images, with no slicing into each image.
*
* Image Collections are normally created automatically when Tiled data is loaded.
*
* @class Phaser.ImageCollection
* @constructor
* @param {string} name - The name of the image collection in the map data.
* @param {integer} firstgid - The first image index this image collection contains.
* @param {integer} [width=32] - Width of widest image (in pixels).
* @param {integer} [height=32] - Height of tallest image (in pixels).
* @param {integer} [margin=0] - The margin around all images in the collection (in pixels).
* @param {integer} [spacing=0] - The spacing between each image in the collection (in pixels).
* @param {object} [properties={}] - Custom Image Collection properties.
*/

var Class = require('../../utils/Class');

var ImageCollection = new Class({

    initialize:

    function ImageCollection (name, firstgid, width, height, margin, spacing, properties)
    {
        if (width === undefined || width <= 0) { width = 32; }
        if (height === undefined || height <= 0) { height = 32; }
        if (margin === undefined) { margin = 0; }
        if (spacing === undefined) { spacing = 0; }

        /**
        * The name of the Image Collection.
        * @property {string} name
        */
        this.name = name;

        /**
        * The Tiled firstgid value.
        * This is the starting index of the first image index this Image Collection contains.
        * @property {integer} firstgid
        */
        this.firstgid = firstgid | 0;

        /**
        * The width of the widest image (in pixels).
        * @property {integer} imageWidth
        * @readonly
        */
        this.imageWidth = width | 0;

        /**
        * The height of the tallest image (in pixels).
        * @property {integer} imageHeight
        * @readonly
        */
        this.imageHeight = height | 0;

        /**
        * The margin around the images in the collection (in pixels).
        * Use `setSpacing` to change.
        * @property {integer} imageMarge
        * @readonly
        */
        // Modified internally
        this.imageMargin = margin | 0;

        /**
        * The spacing between each image in the collection (in pixels).
        * Use `setSpacing` to change.
        * @property {integer} imageSpacing
        * @readonly
        */
        this.imageSpacing = spacing | 0;

        /**
        * Image Collection-specific properties that are typically defined in the Tiled editor.
        * @property {object} properties
        */
        this.properties = properties || {};

        /**
        * The cached images that are a part of this collection.
        * @property {array} images
        * @readonly
        */
        // Modified internally
        this.images = [];

        /**
        * The total number of images in the image collection.
        * @property {integer} total
        * @readonly
        */
        // Modified internally
        this.total = 0;
    },

    /**
    * Returns true if and only if this image collection contains the given image index.
    *
    * @method Phaser.ImageCollection#containsImageIndex
    * @param {integer} imageIndex - The image index to search for.
    * @return {boolean} True if this Image Collection contains the given index.
    */
    containsImageIndex: function (imageIndex)
    {
        return (
            imageIndex >= this.firstgid && imageIndex < (this.firstgid + this.total)
        );

    },

    /**
    * Add an image to this Image Collection.
    *
    * @method Phaser.ImageCollection#addImage
    * @param {integer} gid - The gid of the image in the Image Collection.
    * @param {string} image - The the key of the image in the Image Collection and in the cache.
    */
    addImage: function (gid, image)
    {
        this.images.push({ gid: gid, image: image });
        this.total++;
    }
});

module.exports = ImageCollection;


}
var gameObject = src;
var frame = gameObject.frame;
var gl = gameObject.gl;

var Class = require('../../../utils/Class');
var GameObject = require('../../GameObject');
var Components = require('../../components');
var CONST = require('../../../renderer/webgl/renderers/tilemaprenderer/const');
var StaticTilemapLayerRender = require('./StaticTilemapLayerRender');
var TilemapComponents = require('../components');

var StaticTilemapLayer = new Class({

    Extends: GameObject,

    Mixins: [
        Components.Alpha,
        Components.BlendMode,
        Components.Flip,
        Components.GetBounds,
        Components.Origin,
        Components.RenderTarget,
        Components.ScaleMode,
        Components.Size,
        Components.Texture,
        Components.Transform,
        Components.Visible,
        Components.ScrollFactor,
        StaticTilemapLayerRender
    ],

    initialize:

    /**
     * A StaticTilemapLayer is a game object that renders LayerData from a Tilemap. A
     * StaticTilemapLayer can only render tiles from a single tileset.
     *
     * A StaticTilemapLayer is optimized for speed over flexibility. You cannot apply per-tile
     * effects like tint or alpha. You cannot change the tiles in a StaticTilemapLayer. Use this
     * over a DynamicTilemapLayer when you don't need either of those features.
     *
     * @class StaticTilemapLayer
     * @constructor
     *
     * @param {Scene} scene - [description]
     * @param {Tilemap} tilemap - The Tilemap this layer is a part of.
     * @param {integer} layerIndex - The index of the LayerData associated with this layer.
     * @param {Tileset} tileset - The tileset used to render the tiles in this layer.
     * @param {number} [x=0] - The world x position where the top left of this layer will be placed.
     * @param {number} [y=0] - The world y position where the top left of this layer will be placed.
     */
    function StaticTilemapLayer (scene, tilemap, layerIndex, tileset, x, y)
    {
        GameObject.call(this, scene, 'StaticTilemapLayer');

        /**
         * Used internally by physics system to perform fast type checks.
         * @property {boolean} isTilemap
         * @readonly
         */
        this.isTilemap = true;

        /**
         * The Tilemap that this layer is a part of.
         * @property {Tilemap} map
         */
        this.map = tilemap;

        /**
         * The index of the LayerData associated with this layer.
         * @property {integer} layerIndex
         */
        this.layerIndex = layerIndex;

        /**
         * The LayerData associated with this layer. LayerData can only be associated with one
         * tilemap layer.
         * @property {LayerData} layerIndex
         */
        this.layer = tilemap.layers[layerIndex];
        this.layer.tilemapLayer = this; // Link the LayerData with this static tilemap layer

        /**
         * The Tileset associated with this layer. A tilemap layer can only render from one Tileset.
         * @property {Tileset} tileset
         */
        this.tileset = tileset;

        /**
         * Used internally with the canvas render. This holds the tiles that are visible within the
         * camera.
         * @property {Tileset} culledTiles
         */
        this.culledTiles = [];

        this.vbo = null;
        this.gl = scene.sys.game.renderer.gl ? scene.sys.game.renderer.gl : null;
        this.tilemapRenderer = scene.sys.game.renderer.tilemapRenderer ? scene.sys.game.renderer.tilemapRenderer : null;
        this.resourceManager = this.gl ? scene.sys.game.renderer.resourceManager : null;
        this.bufferData = null;

        this.dirty = true;
        this.vertexCount = 0;

        this.setAlpha(this.layer.alpha);
        this.setTexture(tileset.image.key);
        this.setPosition(x, y);
        this.setSizeToFrame();
        this.setOrigin();
        this.setSize(this.layer.tileWidth * this.layer.width, this.layer.tileHeight * this.layer.height);

        scene.sys.game.renderer.addContextRestoredCallback(this.contextRestore.bind(this));
    },

    /**
     * @return {this}
     */
    contextRestore: function (renderer)
    {
        this.tileTexture = null;
        this.dirty = true;
        this.vbo = null;
        this.gl = renderer.gl;
        this.tilemapRenderer = renderer.tilemapRenderer;
        return this;
    },

    /**
     * Upload the tile data to a VBO.
     *
     * @return {this}
     */
    upload: function (camera)
    {
        var tileset = this.tileset;
        var mapWidth = this.layer.width;
        var mapHeight = this.layer.height;
        var width = this.texture.source[0].width;
        var height = this.texture.source[0].height;
        var mapData = this.layer.data;

        var tile;
        var row;
        var col;
        var texCoords;

        if (this.gl)
        {
            if (this.dirty)
            {
                var gl = this.gl;
                var vbo = this.vbo;
                var bufferData = this.bufferData;
                var bufferF32;
                var voffset = 0;
                var vertexCount = 0;

                if (this.vbo === null)
                {
                    vbo = this.resourceManager.createBuffer(gl.ARRAY_BUFFER, (4 * 6 * (mapWidth * mapHeight)) * 4, gl.STATIC_DRAW);

                    vbo.addAttribute(this.tilemapRenderer.shader.getAttribLocation('a_position'), 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 0);
                    vbo.addAttribute(this.tilemapRenderer.shader.getAttribLocation('a_tex_coord'), 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 8);

                    bufferData = this.bufferData = new ArrayBuffer((4 * 6 * (mapWidth * mapHeight)) * 4);

                    this.vbo = vbo;

                    vbo.bind();
                }

                bufferF32 = new Float32Array(bufferData);

                for (row = 0; row < mapHeight; ++row)
                {
                    for (col = 0; col < mapWidth; ++col)
                    {
                        tile = mapData[row][col];
                        if (tile === null || tile.index === -1) { continue; }

                        var tx = tile.pixelX;
                        var ty = tile.pixelY;
                        var txw = tx + tile.width;
                        var tyh = ty + tile.height;

                        texCoords = tileset.getTileTextureCoordinates(tile.index);
                        if (texCoords === null) { continue; }

                        // Inset UV coordinates by 0.5px to prevent tile bleeding
                        var u0 = (texCoords.x + 0.5) / width;
                        var v0 = (texCoords.y + 0.5) / height;
                        var u1 = (texCoords.x + tile.width - 0.5) / width;
                        var v1 = (texCoords.y + tile.height - 0.5) / height;

                        var tx0 = tx;
                        var ty0 = ty;
                        var tx1 = tx;
                        var ty1 = tyh;
                        var tx2 = txw;
                        var ty2 = tyh;
                        var tx3 = txw;
                        var ty3 = ty;

                        bufferF32[voffset + 0] = tx0;
                        bufferF32[voffset + 1] = ty0;
                        bufferF32[voffset + 2] = u0;
                        bufferF32[voffset + 3] = v0;

                        bufferF32[voffset + 4] = tx1;
                        bufferF32[voffset + 5] = ty1;
                        bufferF32[voffset + 6] = u0;
                        bufferF32[voffset + 7] = v1;

                        bufferF32[voffset + 8] = tx2;
                        bufferF32[voffset + 9] = ty2;
                        bufferF32[voffset + 10] = u1;
                        bufferF32[voffset + 11] = v1;

                        bufferF32[voffset + 12] = tx0;
                        bufferF32[voffset + 13] = ty0;
                        bufferF32[voffset + 14] = u0;
                        bufferF32[voffset + 15] = v0;

                        bufferF32[voffset + 16] = tx2;
                        bufferF32[voffset + 17] = ty2;
                        bufferF32[voffset + 18] = u1;
                        bufferF32[voffset + 19] = v1;

                        bufferF32[voffset + 20] = tx3;
                        bufferF32[voffset + 21] = ty3;
                        bufferF32[voffset + 22] = u1;
                        bufferF32[voffset + 23] = v0;

                        voffset += 24;
                        vertexCount += 6;
                    }
                }

                this.vertexCount = vertexCount;

                vbo.updateResource(bufferData, 0);

                this.dirty = false;
            }

            var renderer = this.tilemapRenderer;

            renderer.shader.setConstantFloat2(renderer.scrollLocation, camera.scrollX, camera.scrollY);
            renderer.shader.setConstantFloat2(renderer.scrollFactorLocation, this.scrollFactorX, this.scrollFactorY);
            renderer.shader.setConstantFloat2(renderer.tilemapPositionLocation, this.x, this.y);

            var cmat = camera.matrix.matrix;

            renderer.shader.setConstantMatrix3x3(
                renderer.cameraTransformLocation,
                [
                    cmat[0], cmat[1], 0.0,
                    cmat[2], cmat[3], 0.0,
                    cmat[4], cmat[5], 1.0
                ]
            );
        }

        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    calculateFacesWithin: function (tileX, tileY, width, height)
    {
        TilemapComponents.CalculateFacesWithin(tileX, tileY, width, height, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {Sprite[]}
     */
    createFromTiles: function (indexes, replacements, spriteConfig, scene, camera)
    {
        return TilemapComponents.CreateFromTiles(indexes, replacements, spriteConfig, scene, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    cull: function (camera)
    {
        return TilemapComponents.CullTiles(this.layer, camera, this.culledTiles);
    },

    /**
    * Destroys this StaticTilemapLayer and removes its link to the associated LayerData.
    */
    destroy: function ()
    {
        // Uninstall this layer only if it is still installed on the LayerData object
        if (this.layer.tilemapLayer === this)
        {
            this.layer.tilemapLayer = undefined;
        }
        this.map = undefined;
        this.layer = undefined;
        this.tileset = undefined;
        GameObject.prototype.destroy.call(this);
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    findByIndex: function (findIndex, skip, reverse)
    {
        return TilemapComponents.FindByIndex(findIndex, skip, reverse, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile|null}
     */
    findTile: function (callback, context, tileX, tileY, width, height, filteringOptions)
    {
        return TilemapComponents.FindTile(callback, context, tileX, tileY, width, height, filteringOptions, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    filterTiles: function (callback, context, tileX, tileY, width, height, filteringOptions)
    {
        return TilemapComponents.FilterTiles(callback, context, tileX, tileY, width, height, filteringOptions, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    forEachTile: function (callback, context, tileX, tileY, width, height, filteringOptions)
    {
        TilemapComponents.ForEachTile(callback, context, tileX, tileY, width, height, filteringOptions, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    getTileAt: function (tileX, tileY, nonNull)
    {
        return TilemapComponents.GetTileAt(tileX, tileY, nonNull, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile}
     */
    getTileAtWorldXY: function (worldX, worldY, nonNull, camera)
    {
        return TilemapComponents.GetTileAtWorldXY(worldX, worldY, nonNull, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    getTilesWithin: function (tileX, tileY, width, height, filteringOptions)
    {
        return TilemapComponents.GetTilesWithin(tileX, tileY, width, height, filteringOptions, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    getTilesWithinWorldXY: function (worldX, worldY, width, height, filteringOptions, camera)
    {
        return TilemapComponents.GetTilesWithinWorldXY(worldX, worldY, width, height, filteringOptions, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Tile[]}
     */
    getTilesWithinShape: function (shape, filteringOptions, camera)
    {
        return TilemapComponents.GetTilesWithinShape(shape, filteringOptions, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {boolean}
     */
    hasTileAt: function (tileX, tileY)
    {
        return TilemapComponents.HasTileAt(tileX, tileY, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {boolean}
     */
    hasTileAtWorldXY: function (worldX, worldY, camera)
    {
        return TilemapComponents.HasTileAtWorldXY(worldX, worldY, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    renderDebug: function (graphics, styleConfig)
    {
        TilemapComponents.RenderDebug(graphics, styleConfig, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setCollision: function (indexes, collides, recalculateFaces)
    {
        TilemapComponents.SetCollision(indexes, collides, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setCollisionBetween: function (start, stop, collides, recalculateFaces)
    {
        TilemapComponents.SetCollisionBetween(start, stop, collides, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setCollisionByExclusion: function (indexes, collides, recalculateFaces)
    {
        TilemapComponents.SetCollisionByExclusion(indexes, collides, recalculateFaces, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setTileIndexCallback: function (indexes, callback, callbackContext)
    {
        TilemapComponents.SetTileIndexCallback(indexes, callback, callbackContext, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {this}
     */
    setTileLocationCallback: function (tileX, tileY, width, height, callback, callbackContext)
    {
        TilemapComponents.SetTileLocationCallback(tileX, tileY, width, height, callback, callbackContext, this.layer);
        return this;
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    tileToWorldX: function (tileX, camera)
    {
        return TilemapComponents.TileToWorldX(tileX, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    tileToWorldY: function (tileY, camera)
    {
        return TilemapComponents.TileToWorldY(tileY, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Vector2}
     */
    tileToWorldXY: function (tileX, tileY, point, camera)
    {
        return TilemapComponents.TileToWorldXY(tileX, tileY, point, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    worldToTileX: function (worldX, snapToFloor, camera)
    {
        return TilemapComponents.WorldToTileX(worldX, snapToFloor, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {number}
     */
    worldToTileY: function (worldY, snapToFloor, camera)
    {
        return TilemapComponents.WorldToTileY(worldY, snapToFloor, camera, this.layer);
    },

    /**
     * See component documentation.
     *
     * @return {Vector2}
     */
    worldToTileXY: function (worldX, worldY, snapToFloor, point, camera)
    {
        return TilemapComponents.WorldToTileXY(worldX, worldY, snapToFloor, point, camera, this.layer);
    }

});

module.exports = StaticTilemapLayer;


var Color = require('./Color');

var ObjectToColor = function (input)
{
    return new Color(input.r, input.g, input.b, input.a);
};

module.exports = ObjectToColor;


/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* A Blitter Game Object.
*
* The Blitter Game Object is a special type of Container, that contains Blitter.Bob objects.
* These objects can be thought of as just texture frames with a transform, and nothing more.
* Bobs don't have any update methods, or the ability to have children, or any kind of special effects.
* They are essentially just texture renderers, and the Blitter object creates and manages them.
*
* @class
*/
Phaser.GameObject.Blitter = function (state, parent)
{
    Phaser.GameObject.call(this, state, 0, 0, null, null, parent);

    this.type = Phaser.BLITTER;

    this.children = new Phaser.Component.Children(this);
};

Phaser.GameObject.Blitter.prototype = Object.create(Phaser.GameObject.prototype);
Phaser.GameObject.Blitter.prototype.constructor = Phaser.GameObject.Blitter;

Phaser.GameObject.Blitter.prototype.preUpdate = function ()
{
    if (this.parent)
    {
        this.color.worldAlpha = this.parent.color.worldAlpha;
    }
};

Phaser.GameObject.Blitter.prototype.create = function (x, y, key, frame, visible, index)
{
    if (visible === undefined) { visible = true; }
    if (index === undefined) { index = 0; }

    var bob = new Phaser.GameObject.Blitter.Bob(this, x, y, key, frame, visible);

    this.children.addAt(bob, index, false);

    return bob;
};

Phaser.GameObject.Blitter.prototype.createFromCallback = function (callback, quantity, key, frame, visible)
{
    var bobs = this.createMultiple(quantity, key, frame, visible);

    for (var i = 0; i < bobs.length; i++)
    {
        var bob = bobs[i];

        callback.call(this, bob, i);
    }

    return bobs;
};

Phaser.GameObject.Blitter.prototype.createMultiple = function (quantity, key, frame, visible)
{
    if (frame === undefined) { frame = 0; }
    if (visible === undefined) { visible = true; }

    if (!Array.isArray(key))
    {
        key = [ key ];
    }

    if (!Array.isArray(frame))
    {
        frame = [ frame ];
    }

    var bobs = [];
    var _this = this;

    key.forEach(function (singleKey)
    {
        frame.forEach(function (singleFrame)
        {
            for (var i = 0; i < quantity; i++)
            {
                bobs.push(_this.create(0, 0, singleKey, singleFrame, visible));
            }
        });
    });

    return bobs;
};


/**
 * [description]
 *
 * @function Phaser.Geom.Line.SetToAngle
 * @since 3.0.0
 *
 * @param {Phaser.Geom.Line} line - [description]
 * @param {number} x - [description]
 * @param {number} y - [description]
 * @param {number} angle - [description]
 * @param {number} length - [description]
 *
 * @return {Phaser.Geom.Line} [description]
 */
var SetToAngle = function (line, x, y, angle, length)
{
    line.x1 = x;
    line.y1 = y;

    line.x2 = x + (Math.cos(angle) * length);
    line.y2 = y + (Math.sin(angle) * length);

    return line;
};

module.exports = SetToAngle;


var frame = gameObject.tileset.image.get();