Use RenderTexture without addChild

Here is my code:

SpriteFrame* frame = SpriteFrameCache::getInstance()->getSpriteFrameByName("big.png");
Sprite* sprite = Sprite::createWithSpriteFrame(frame);
sprite->setAnchorPoint(Vec2::ZERO);
TextBMFont* text = TextBMFont::create("00000", "fonts/gold_big.fnt");
const Vec2 spriteCenter = static_cast<Vec2>(sprite->getContentSize() * 0.5f);
text->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
text->setPosition(spriteCenter);
sprite->addChild(text);
addChild(sprite, -10); // I need this, WHY?

RenderTexture* rt = RenderTexture::create(sprite->getContentSize().width, sprite->getContentSize().height);
rt->getSprite()->getTexture()->setAntiAliasTexParameters();
rt->retain();
rt->begin();
sprite->visit();
rt->end();

If I do addChild(sprite, -10); then I get texture rendered properly in rt, otherwise it renders nothing. Why I need to add child? How to avoid polluting the screen when I need to render in a texture (RenderTexture) and only then add the rendered final thing to the scene?

EDIT: I have tried to add child, render and then remove like this:

.........
.........
rt->end();
Director::getInstance()->getRenderer()->render();
removeChild(sprite, true);

but this rendered nothing too.

How are you testing if the rt is being rendered to? Is it being added to the scene or are you inspecting GL state?

Want to make sure what you’re seeing isn’t simply the sprite rendering normally.

@almax27 thank you for your response. I have another sprite and I do the following to see the rendered texture:

setTextureRect(rt->getSprite()->getTextureRect());
setTexture(rt->getSprite()->getTexture()); 

and when the above mentioned add child is done I see the rendered texture, otherwise the sprite to whom I set texture and its rect gets and empty texture. :frowning:

I have got the solution:

setFlippedY(true);
setSpriteFrame(rt->getSprite()->getSpriteFrame());

But I don’t understand what is the difference if I setSpriteFrame, or I just set texture and texture rect. Isn’t this unexpected?

Try calling draw() on your sprite instead of visit(), I think your sprite is getting offset by the RenderTexture transform.

Node uses matrix stack

void Node::visit()
{
    auto renderer = _director->getRenderer();
    auto& parentTransform = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    visit(renderer, parentTransform, true);
}

And so does RenderTexture

void RenderTexture::onBegin()
{
    //
    Director *director = Director::getInstance();
    
    _oldProjMatrix = director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, _projectionMatrix);
    
    _oldTransMatrix = director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _transformMatrix);

    ...
}

Not sure I understand what you mean? Does addChild change something for RenderTexture transform?

And do you have a clue why

setFlippedY(true);
setSpriteFrame(rt->getSprite()->getSpriteFrame());

can fix my problem?

Well setSpriteFrame simply calls ‘setTexture()’ and applies the spritesheet offset.

So only other thing I can think of is the order you call setTexture() and setTextureRect(), because setTexture() calls setTextureRect() internally and will probably override your setTextureRect() call.

Try calling setTexture() first and setTextureRect() second.

Oh and the Y-Flip is necessary because you are rendering to GL space using cocos coordinates. They use different origins I believe.

(Edit) After a bit of reading I think that while GL and cocos2d-x use bottom left origins, the OS window will use a top left origin. And since the flip is abstracted for all render paths by cocos to account for this, we end up flipping when rendering to texture too.

I dont think you really use render texture feature to show the sprite, but you’re using addchild to add the sprite to the screen itself.

You should think Render Texture as a buffer or a hidden board, where you draw some/may sprites on it to make it into another sprite with effect/combination of many sprites…

In your case, you dont need to this line addChild(sprite, -10); but you’ll need some thing like this after rt->end();

auto anotherSpriteWithRenderTextureEffect = Sprite::createWithTexture(rt->getSprite()->getTexture()); //now you must create new sprite contains the texture from Render Texture

anotherSpriteWithRenderTextureEffect->setFlippedY(true); //like @almax27 explain

this->addChild(anotherSpriteWithRenderTextureEffect); //this is necessary because you need this to display this sprite

Imagine when rt->end(); is called I am in a init() method of a child class of Sprite. And then I do this:

setTextureRect(rt->getSprite()->getTextureRect());
setTexture(rt->getSprite()->getTexture());

This should change the texture of this sprite, right? But it does not. But when I do the following instead:

setFlippedY(true);
setSpriteFrame(rt->getSprite()->getSpriteFrame());

it DOES change the this sprite texture (i.e. view). So, I use it, without using another sprite anotherSpriteWithRenderTextureEffect as you mention in your code. The problem is that it is not being rendered. Hope now it is clear how I use it, cause I have not described it in my previous comments.