Creating a texture dynamically

I want to be able to pick multiple tiles from my tilesheet and “stamp” them onto a new texture which will become a sprite. I can do it once, but when I try to do it a second time the first stamp is erased from the texture. The plan is to do all of this in a loop when it’s done (things are hardcoded right now).

Or should is it better (or faster) to manipulate the texture data as discussed here?
Is it possible to "erase" some pixels from a sprite?

Here’s the code code I’ve been testing with:

// src is a sprite
Rect rectStart = Rect((colStart * TILE_SIZE) + offset.x, (rowStart * TILE_SIZE + offset.y), 16, 16);
auto texture = RenderTexture::create(w, h, src->getTexture()->getPixelFormat());
auto stamp = RenderTexture::create(16, 16, src->getTexture()->getPixelFormat());

stamp->clear(0, 0, 0, 0);
stamp->begin();
src->setFlipY(true);
src->setPosition(-rectStart.origin.x, -rectStart.origin.y);
src->visit();
stamp->end();

texture->clear(1.0f, 0, 0, 1.0f);
texture->begin();
stamp->setPosition(8, 8);
stamp->visit(Director::getInstance()->getRenderer(), this->getParentToNodeTransform(), FLAGS_TRANSFORM_DIRTY);
texture->end();

Rect rectEnd = Rect((colMid * TILE_SIZE) + offset.x, (rowMid * TILE_SIZE + offset.y), 16, 16);
	
stamp->clear(0, 0, 0, 0);
stamp->begin();
src->setPosition(-rectEnd.origin.x, -rectEnd.origin.y);
src->visit();
stamp->end();

texture->begin();
stamp->setPosition(24, 8);
stamp->visit(Director::getInstance()->getRenderer(), this->getParentToNodeTransform(), FLAGS_TRANSFORM_DIRTY);
texture->end();

Have you checked the stamp texture to ensure that it has the correct content when you copy the source into it the second time?

Also, is there a particular reason you don’t just copy the source directly into the final texture, instead of using the stamp texture as the intermediate step? You can always create a Sprite object that has the correct rectangle slice information from the bigger source texture (size 16x16 in your case, starting at the x and y of your choosing), and then draw that into the final RenderTexture.

I tried using two stamp textures at one point, and if I only used one stamp (16x16px), it looked ok. Then if I did one more (16x16px, one tile to the right from the first) the resulting texture had gotten two 16x16px tiles that looked the same. I could not figure out how to make them look different and had to ask here for help.

I’m not very experienced with this, but what methods do I use to “copy the source directly into the final texture”?

If I create sprites like you suggest, is the method still the same as I did; using a RenderTexture and calling visit on the sprites?

After reading your initial post, and seeing the code you provided, it seemed clear what you were trying to do, but now with your new response, things are less clear.

Firstly, what are your source images? It’s best if you provide them in response on here. Is the source image 16x16 in size, or is it a large image that you want to copy a 16x16 texture rectangle from?

What I meant by copy the source is that in your code, you have a “src” variable, but you didn’t provide what it actually is, so I assumed it is a sprite. So, if that is the case, then it seems pointless to copy that image into the stamp RenderTexture, when you can put it directly into the final RenderTexture (which you’ve named “texture”, so perhaps renaming it so something more meaningful would help).

So, this is the code, assuming src is a texture that you want to stamp multiple times (you would do this in some kind of loop though…):

auto outputTexture = RenderTexture::create(w, h, src->getTexture()->getPixelFormat());

auto src = Sprite::createXXXXX() (whatever method you choose to create it with)

outputTexture->beginWithClear(0, 0, 0, 0);
src->setFlipY(true);
src->setPosition(8, 8);
src->visit();
outputTexture->end();

outputTexture->begin();
src->setPosition(24, 8);
src->visit();
outputTexture->end();

Now, if src is only to be used once, and you want a different image to be stamped each time, that image coming from a larger texture (like a sprite sheet), then you would need to create the src sprite each time from the sprite sheet texture, using this method:

auto src = Sprite::createWithTexture(srcTexture, rect, rotated);

and then you would use that sprite inside the begin() and end() of the RenderTexture block.

I managed to make it work, thanks! Didn’t know about that three-parameter method for sprites. Might come in handy for other stuff too.

So, here’s the code I came up with:

auto src = Sprite::create("images/tilesheet.png");
auto srcSize = src->getTexture()->getContentSizeInPixels();
src->setAnchorPoint(Vec2::ZERO);

int rowStart = (int)(tileIdxStart / ((int)srcSize.width / TILE_SIZE));
int colStart = (int)(tileIdxStart % ((int)srcSize.width / TILE_SIZE));

int rowMid = (int)(tileIdxMid / ((int)srcSize.width / TILE_SIZE));
int colMid = (int)(tileIdxMid % ((int)srcSize.width / TILE_SIZE));

int rowEnd = (int)(tileIdxEnd / ((int)srcSize.width / TILE_SIZE));
int colEnd = (int)(tileIdxEnd % ((int)srcSize.width / TILE_SIZE));

auto tex = RenderTexture::create(w, h, src->getTexture()->getPixelFormat());
tex->beginWithClear(0, 0, 0, 0);

int midWidth = w - 2 * TILE_SIZE;
int numMidTiles = (midWidth / TILE_SIZE);
int numIterations = numMidTiles + 2; /* for start and end tiles */
for (size_t i = 0; i < numIterations; i++) {
	Rect rect;
	if (i == 0) {
		rect = Rect((colStart * TILE_SIZE) + offset.x, (rowStart * TILE_SIZE + offset.y), TILE_SIZE, TILE_SIZE);
	} else if (i == numIterations -1) {
		rect = Rect((colEnd * TILE_SIZE) + offset.x, (rowEnd * TILE_SIZE + offset.y), TILE_SIZE, TILE_SIZE);
	} else {
		rect = Rect((colMid * TILE_SIZE) + offset.x, (rowMid * TILE_SIZE + offset.y), TILE_SIZE, TILE_SIZE);
		}

	auto tmp = Sprite::createWithTexture(src->getTexture(), rect, false);
	tmp->setFlipY(true);
	tmp->setPosition(tmp->getPosition() + Vec2(TILE_SIZE * 0.5 + (i * TILE_SIZE), TILE_SIZE * 0.5));
	tmp->visit();
}

tex->end();
auto ret = Sprite::createWithTexture(tex->getSprite()->getTexture());
ret->getTexture()->setAliasTexParameters();
return ret;
1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.