Combining Textures for a Sprite

Hello all,

Currently I am extending a Sprite and initializing it as follows:

this._super();
this.initWithFile('./Resources/sprite1.png', new cc.Rect(0, 0, 128, 128));

However, I want to overlay sprite1.png with sprite2.png and use THAT for my Sprite texture. I thought of creating a Node with two child Sprites, however this will complicate things heavily when I start thinking of animations.

My goal is something like:

texture1 = cc.TextureCache.getInstance().textureForKey('./Resources/sprite1.png');
texture2 = cc.TextureCache.getInstance().textureForKey('./Resources/sprite2.png');
texture = texture1.combineWith(texture2);

And from there I would initialize the Sprite with the combined texture as opposed to one individual file.

Of course, it will probably be more complicated than what is above. Does anybody happen to have thoughts on the best way to approach this problem?

I’ve found some hope with CCRenderTexture.

/**
 * cc.RenderTexture is a generic rendering target. To render things into it,
 * simply construct a render target, call begin on it, call visit on any cocos
 * scenes or objects to render them, and call end. For convienience, render texture
 * adds a sprite as it's display child with the results, so you can simply add
 * the render texture to your scene and treat it like any other CocosNode.
 * There are also functions for saving the render texture to disk in PNG or JPG format.
 * @class
 * @extends cc.Node
 */

So the process would look something like:

var texture = new cc.RenderTexture();
texture.initWithWidthAndHeight(WIDTH, HEIGHT, cc.IMAGE_FORMAT_PNG, 0);
texture.begin();
texture.visit(/* OBJECT GOES HERE */);
texture.visit(/* OBJECT GOES HERE */);
texture.end();

Though I’m having a few issues that I’m hoping someone might be able to comment on.
* cc.IMAGE_FORMAT_PNG is not valid though it seems to be defined in CCRenderTexture.js. Workaround thus far is using the hardcoded value: 1.

  • /* OBJECT GOES HERE **/ I’m not sure that this can be a texture. Workaround will be to create a Sprite, and call visit on the Sprites.
    ** CCRenderTexture.js seems to be having some issues. Web Inspector reports Uncaught ReferenceError: kmGLPushMatrix is not defined in CCRenderTexture.js:250. Does anyone know of any additional config or dependencies that might be required?

  • In this case, my character will no longer extend Sprite, but RenderTexture. Alternatively, could possibly retrieve the RenderTexture as an image, and try constructing a texture from that data, then create a Sprite of that. Will explore that if necessary but don’t think it will be.

EDIT: Converting to a Sprite may be necessary still in order to leverage animations on the combined Sprite sheet that RenderTexture is aimed at creating.

I guess the primary issue is the third bullet above. If anybody has any thoughts, I’d be very happy to hear them.

Cheers!

So this ended up being a lot easier than I thought. A lot of this was based on scoping out the source code for RenderTexture, then only taking the parts I needed. If anybody has feedback on a better implementation, please do let me know.

To start, my Character Class extends Sprite. Its constructor is as follows:

ctor: function () {
    'use strict';

    this._super();
    this.initWithTexture(this.generateTexture(), new cc.Rect(0, 0, 128.0, 128.0));

    /* Other initialization like scale, position, etc. goes here. */
},

And the heart of generateTexture looks like so:

generateTexture: function () {
    'use strict';

    textures = [
        './Resources/sprite1.png',
        './Resources/sprite2.png'
    ];

    canvas = document.createElement('canvas');
    context = canvas.getContext('2d');

    for (n = 0; n < textures.length; n = n + 1) {
        context.drawImage(cc.TextureCache.getInstance().textureForKey(textures[n]), 0, 0);
    }

    return canvas;
},

I’m not setting the width and height of the canvas. I haven’t gotten far enough to find out whether I need to. If I did, I would be setting it to the common width and height of the image resources I am overlapping; or the largest resource I guess. Note that the resources have been preloaded.

The beauty is that Cocos2d-HTML5 lets us use a canvas as a texture. Knowing this opens up a lot of possibilities.

EDIT: Setting canvas.width and canvas.height does appear to be necessary.

Last one on this (hopefully.) I was having issues with the above implementation where I just return a canvas. Specifically, initWithTexture did not care about my Rect and would always assume an offset of (0, 0). I could not even use setTextureRect to update it. So I’ve made a small change and, instead of returning canvas, we return an Image constructed from the canvas. Final code (for now) that seems to be working is as follows:

    ctor: function () {
        'use strict';

        this._super();
        this.initWithTexture(this.generateTexture([
            './Resources/image1.png',
            './Resources/image2.png',
            './Resources/image3.png'
        ]), new cc.Rect(512.0, 512.0, 128.0, 128.0)); /* Now the offset works. */
    },

    generateTexture: function (textures) {
        'use strict';

        var canvas, context, texture, n;

        canvas = document.createElement('canvas');
        canvas.width = SPRITE_SHEET_WIDTH;   /* width and height are common to all image resources being loaded. */
        canvas.height = SPRITE_SHEET_HEIGHT;
        context = canvas.getContext('2d');

        for (n = 0; n < textures.length; n = n + 1) {
            context.drawImage(cc.TextureCache.getInstance().textureForKey(textures[n]), 0, 0);
        }

        texture = new Image();
        texture.src = canvas.toDataURL("image/png");

        return texture;
    },
1 Like