Rendering to texture (RTT) in CEGUI

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search

Written for CEGUI 0.8


Works with versions 0.8.x (stable)

Works with latest CEGUI stable!

Rendering to a texture (RTT) using a CEGUI GUIContext

Since version 0.8 the new GUIContext class allows to render its content into a texture instead of rendering it normally into the framebuffer of your display. This allows to directly render UIs to a texture, and for example display it inside your 3D world, as you would regularly do with a texture.


CEGUI::System& ceguiSystem = CEGUI::System::getSingleton();
 
//Taking some random values as example for a GUIContext size.
int width = 1024;
int height = 800;
CEGUI::Sizef size(static_cast<float>(width), static_cast<float>(height));
 
// We create a CEGUI texture target and create a GUIContext that will use it.
CEGUI::TextureTarget* renderTextureTarget = ceguiSystem.getRenderer()->createTextureTarget();
renderTextureTarget->declareRenderSize(size);
CEGUI::GUIContext& renderGuiContext = ceguiSystem.createGUIContext(static_cast<CEGUI::RenderTarget&>(*renderTextureTarget) );



For rendering you need to call the rendering function of your new GuiContext directly. Here is an example of how to call the renderer and the guicontext in the right order when rendering:

    CEGUI::Renderer* gui_renderer(gui_system.getRenderer());
    gui_renderer->beginRendering();
 
    renderTextureTarget->clear();
    renderGuiContext.draw();
 
    gui_renderer->endRendering();

Also do not forget to correctly update CEGUI and the GUIContext with all timepulses and inputs, just as you would do it normally but by using your new GUIContext instead of the default one!

If you want to retrieve the renderer-specific texture from a CEGUI texture you will need to cast it:

For example for OpenGL:

CEGUI::OpenGLTexture& glTexture = static_cast<CEGUI::OpenGLTexture&>(renderTextureTarget->getTexture());

After that you can use the member function to receive whatever render-specific traits you need, such as the textureID in Opengl as an unsigned integer, or an Ogre::Texture*, etc. ...


Displaying a (RTT) texture inside your GUI using a CEGUI window

The following section can be used to display a texture that you created in your Renderer, and to which you might be rendering continuously (RTT), inside a window of a CEGUI GUI. For doing so, you need to create a CEGUI Image based on the rendered texture. The following code snippet shows you the full process of creating the CEGUI::Image and setting it up for your Renderer's texture:

// We create a CEGUI Texture using the renderer you use:
CEGUI::Texture& texture = gui_renderer->createTexture("MyCEGUITextureName");
 
// Now we need to cast it to the CEGUI::Texture superclass which matches your Renderer. This can be CEGUI::OgreTexture or CEGUI::OpenGLTexture, depending on the renderer you use in your application
// We will use Ogre here as an example
CEGUI::OgreTexture& rendererTexture = static_cast<CEGUI::OgreTexture&>(texture);
// Now we can set the appropriate Ogre::Texture for our CEGUI Texture
rendererTexture.setOgreTexture(whateverTextureYouWannaUse, false);
 
// We create a BasicImage (later called BitmapImage)
CEGUI::BasicImage* image = static_cast<CEGUI::BasicImage*>(&CEGUI::ImageManager::getSingleton().create("BasicImage", "MyImageGroup/MyImageName"));
 
// Texture coord flipping is necessary due to differences between renderers regarding top or bottom being the origin.
// In 0.8.5+ you can use the isTexCoordSystemFlipped() function of your specific Renderer (e.g. CEGUI::OpenGLRenderer, but not CEGUI::Renderer)
// Starting with 1.0 you can directly call isTexCoordSystemFlipped() for any Renderer instance.
// In 0.8.4- none of these functions are available directly, in which case you should use isTextureTargetVerticallyFlipped = true for OpenGL (including OpenGL and OpenGL ES) and else =false.
OgreRenderer* ogreRenderer = static_cast<OgreRenderer*>(ceguiSystem.getRenderer());
bool isTextureTargetVerticallyFlipped = ogreRenderer->isTexCoordSystemFlipped();
CEGUI::Rectf imageArea;
 
if (isTextureTargetVerticallyFlipped)
    imageArea= CEGUI::Rectf(0.0f, textureHeight, textureWidth, 0.0f);
else
    imageArea= CEGUI::Rectf(0.0f, 0.0f, textureWidth, textureHeight);
 
// This is necessary to define the image's size. Regardless of flipping or not you need to set the size. Multiple images can be associated with a single texture.
image->setArea(imageArea);
// You most likely don't want autoscaling for RTT images. If you display it in stretched-mode inside a button or Generic/Image widget, then this setting does not play a role anyways.
image->setAutoScaled(ASM_Disabled);
 
// Of course we need to set the texture for the image too
image->setTexture(&rendererTexture);


Important: If you modify the CEGUI::Image or the CEGUI::Texture (OpenGLTexture, OgreTexture, etc), for example when changing CEGUI:Texture's internal texture, you will have to call the CEGUI::Window's invalidate() function. If adjusting Image* or Texture* instances directly, the CEGUI Window will not be notified about the changes, which requires a manual update call to the windows using the instance. For example:

ceguiOgreTexture->setOgreTexture(someNewOgreTexture); //CEGUI::OgreTexture   and parameter   Ogre::TexturePtr
windowUsingTheTexture->invalidate(); // CEGUI::Window*

Important: Please ensure that the window that displays the RTT Texture (or any other parent) has AutoRenderingSurface switched off. Typically FrameWindows have this property activated. If it is on, the entire CEGUI::Window might be cached in situations where you actually want it to be rendered continuously without caching (such as when rendering to the texture continuously). When the window is cached, only an invalidation (by calling invalidate() or performing a click or move) will trigger a render update. Normally you only need to call invalidate on the window when pointing a CEGUI::Texture* instance to a new OpenGL or Ogre Texture, or similar.

Rendering a layout directly to any Ogre material

The following code can be used as an example of how to render a loaded layout directly to an arbitrary Ogre material by replacing on of its texture units with the layout. This obviously only works exactly like that for Ogre, but I guess it is similar for other libraries.

First of all, we need to load the layout, create our own GuiContext and assign the layout to it.

// Load the layout and put it into a default window
CEGUI::Window* root = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "root");
CEGUI::Window* window = CEGUI::WindowManager::getSingleton().loadLayoutFromFile(layoutPath);
root->addChild(_window );
 
// Create a texture target to render to
CEGUI::System& ceguiSystem = CEGUI::System::getSingleton();
CEGUI::OgreRenderer* ogreRenderer = static_cast<CEGUI::OgreRenderer *>(ceguiSystem.getRenderer());
CEGUI::Sizef size(static_cast<float>(yourWidth), static_cast<float>(yourHeight));
CEGUI::TextureTarget* textureTarget = ogreRenderer->createTextureTarget();
textureTarget ->declareRenderSize(size);
 
// Create the gui context and assign the root to it
CEGUI::GuiContext& context = ceguiSystem.createGUIContext(static_cast<CEGUI::RenderTarget&>(_renderTextureTarget) );
context->setRootWindow(root);

The above part should be mostly identical for any library.

The next thing we want to do in Ogre is to get the name of the Ogre::Texture the CEGUI::TextureTarget uses to replace a texture unit in a material with.

// Get the Ogre texture
CEGUI::Texture& renderTargetTexture = _renderTextureTarget->getTexture();
Ogre::TexturePtr ogreTexture = static_cast<CEGUI::OgreTexture&>(renderTargetTexture).getOgreTexture();
 
// Find the material
Ogre::MaterialPtr matPtr = Ogre::MaterialManager::getSingleton().getByName("Material");
// Find the technique
Ogre::Technique* technique = matPtr->getTechnique("Tech");
// Find the pass
Ogre::Pass* pass = technique->getPass("Pass");
// Find the texture unit and replace the texture
Ogre::TextureUnitState* textureUnit = pass->getTextureUnitState("Texture");
textureUnit->setTextureName(ogreTexture->getName());

The only thing remaining is to call render it all whenever you want to update the texture:

ogreRenderer->beginRendering();
textureTarget->clear();
context->draw();
ogreRenderer->endRendering();

Possible problems: Nothing is showing up at all: Did you call context->draw() at least once? It does not have to be every frame, only when you want to update the texture.

The layout's position is wrong: Make sure to place your loaded layout in a default window (or another correct container) and to set that container as the root of the GuiContext.

The background is not transparent: Have a look if everything in your shaders and rendering pipeline is correct. I had the problem that I was re-assigning the texture to the texture unit every frame, which caused the GUI to appear without proper alpha.