Newer
Older
pixi.js / src / core / display / SpriteBatch.js
@Chad Engler Chad Engler on 28 Dec 2014 5 KB initial move to new module folders
var DisplayObjectContainer = require('./DisplayObjectContainer'),
    WebGLFastSpriteBatch = require('../renderers/webgl/utils/WebGLFastSpriteBatch');

/**
 * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed,
 * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced
 * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation).
 * Any other functionality like interactions, tinting, etc will not work on sprites in this batch.
 *
 * It's extremely easy to use :
 *
 * ```js
 * var container = new SpriteBatch();
 *
 * stage.addChild(container);
 *
 * for(var i = 0; i < 100; ++i) {
 *     var sprite = new PIXI.Sprite.fromImage("myImage.png");
 *     container.addChild(sprite);
 * }
 * ```
 *
 * And here you have a hundred sprites that will be renderer at the speed of light.
 *
 * @class
 * @namespace PIXI
 */

//TODO RENAME to PARTICLE CONTAINER?
function SpriteBatch() {
    DisplayObjectContainer.call(this);

    this.ready = false;
}

SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype);
SpriteBatch.prototype.constructor = SpriteBatch;

/**
 * Initialises the spriteBatch for WebGL
 *
 * @param gl {WebGLContext} the current WebGL drawing context
 */
SpriteBatch.prototype.initWebGL = function (gl) {
    // TODO only one needed for the whole engine really?
    this.fastSpriteBatch = new WebGLFastSpriteBatch(gl);

    this.ready = true;
};

/**
 * Updates the object transform for rendering
 *
 * @private
 */
SpriteBatch.prototype.updateTransform = function () {
    // TODO don't need to!
    this.displayObjectUpdateTransform();
    //  PIXI.DisplayObjectContainer.prototype.updateTransform.call( this );
};

/**
 * Renders the object using the WebGL renderer
 *
 * @param renderSession {RenderSession}
 * @private
 */
SpriteBatch.prototype._renderWebGL = function (renderSession) {
    if (!this.visible || this.alpha <= 0 || !this.children.length) {
        return;
    }

    if (!this.ready) {
        this.initWebGL(renderSession.gl);
    }

    renderSession.spriteBatch.stop();

    renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader);

    this.fastSpriteBatch.begin(this, renderSession);
    this.fastSpriteBatch.render(this);

    renderSession.spriteBatch.start();
};

/**
 * Renders the object using the Canvas renderer
 *
 * @param renderSession {RenderSession}
 * @private
 */
SpriteBatch.prototype._renderCanvas = function (renderSession) {
    if (!this.visible || this.alpha <= 0 || !this.children.length) {
        return;
    }

    var context = renderSession.context;
    var transform = this.worldTransform;
    var isRotated = true;

    context.globalAlpha = this.worldAlpha;

    this.displayObjectUpdateTransform();

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

        if (!child.visible) {
            continue;
        }

        var frame = child.texture.frame;

        context.globalAlpha = this.worldAlpha * child.alpha;

        if (child.rotation % (Math.PI * 2) === 0) {
            // this is the fastest  way to optimise! - if rotation is 0 then we can avoid any kind of setTransform call
            if (isRotated) {
                context.setTransform(
                    transform.a,
                    transform.b,
                    transform.c,
                    transform.d,
                    transform.tx,
                    transform.ty
                );

                isRotated = false;
            }

            context.drawImage(
                child.texture.baseTexture.source,
                frame.x,
                frame.y,
                frame.width,
                frame.height,
                ((child.anchor.x) * (-frame.width * child.scale.x) + child.position.x  + 0.5) | 0,
                ((child.anchor.y) * (-frame.height * child.scale.y) + child.position.y  + 0.5) | 0,
                frame.width * child.scale.x,
                frame.height * child.scale.y
            );
        }
        else {
            if (!isRotated) {
                isRotated = true;
            }

            child.displayObjectUpdateTransform();

            var childTransform = child.worldTransform;

            if (renderSession.roundPixels) {
                context.setTransform(
                    childTransform.a,
                    childTransform.b,
                    childTransform.c,
                    childTransform.d,
                    childTransform.tx | 0,
                    childTransform.ty | 0
                );
            }
            else {
                context.setTransform(
                    childTransform.a,
                    childTransform.b,
                    childTransform.c,
                    childTransform.d,
                    childTransform.tx,
                    childTransform.ty
                );
            }

            context.drawImage(
                child.texture.baseTexture.source,
                frame.x,
                frame.y,
                frame.width,
                frame.height,
                ((child.anchor.x) * (-frame.width) + 0.5) | 0,
                ((child.anchor.y) * (-frame.height) + 0.5) | 0,
                frame.width,
                frame.height
            );
        }
    }
};