Note: The example code below relies on a new function added in branches/v0-7 @ r2470.I worked up a demo for this and early on it became apparent that there were vaious 'levels' of implementation for this. Basically, the code you previously posted is the simplest of the levels; you say your appempts to render like that "didn't turn out well" - I wonder what that means exactly?
![Wink ;)](./images/smilies/icon_wink.gif)
My initial code was basically the same as you posted - the content was rendered to the surface of the FrameWindow overdrawing the other window content. Adjusting the dimensions of the added viewport allows us to target specific areas of the texture so we can more easily share the surface with regular CEGUI content. After doing this, and playing around some more, there are two main issues with this approach:
1) Because the rendering is by default controlled by Ogre, things are drawn out of sequence as far as CEGUI is concerned, and this means you get visible flickering as the CEGUI and viewport content are drawn over each other.
2) Resizing a window that we have 'hooked into' in this fashion can result in the RenderTarget being deleted and/or reallocated in order to cope with the resize; the best case here is that the viewport content no longer gets drawn to the Window surface, and the worst case is that we seg (depending on how we have things arranged).
So, to tackle these issues we have to jump some hoops. I'll briefly discuss solutions to these issues separately.
1) The basic solution to this is to call setAutoUpdated(false) for the Ogre::RenderTarget that we extracted from the CEGUI::RenderingSurface:
Code: Select all
// tell Ogre not to draw this target automatically (else you get
// visible flickering).
mRttTexture->setAutoUpdated(false);
We'll then update it manually at an appropriate time. Ok, so when is an appropriate time and how do we know when it arrives?
An appropriate time would be after all CEGUI texture caching is done, but before the results of that is actually rendered to the screen. Each CEGUI::RenderingSurface has a number of rendering queues, and where these exist (they're created on the fly upon first usage), events are fired before and after each queue is rendered for each individual CEGUI::RenderingSurface. We can use these events to be informed that the rendering is about to be done to the screen - so we subscribe a handler to CEGUI::RenderingSurface::EventRenderQueueStarted for the
default rendering root, we do not subscribe to the surface we're going to draw to, because that only gets updated when the window changes, so we use something higher up in the hierarchy and for our purposes here, the RenderingRoot (which is itself a RenderingSurface) returned via CEGUI::Renderer::getDefaultRenderingRoot is what should use - and this would suffice for many, many other scenarios too:
Code: Select all
// Since we set the Ogre RTT above to not be auto updated, we'll use an
// event subscription to decide when to render that content manually.
// Basically this needs to be done after other UI content is drawn but
// before anything gets put on screen. We need to use an appropriate
// RenderingSurface for this - not the surface used for the window we
// will render directly to - but something higher up. For this demo
// (and many other scenarios) the RenderingRoot will suffice. We use
// the EventRenderQueueStarted because we want to update the content
// /before/ it's drawn to the surface (here that would be the screen).
mRenderer->getDefaultRenderingRoot().subscribeEvent(
RenderingSurface::EventRenderQueueStarted,
Event::Subscriber(&OgreRTTDemo::updateRTTContent, this));
Within the handler we subscribe we check to see which queue the event is for. As mentioned queues are created as-needed, so checking for a queue that has not been 'touched' elsewhere will not result in any match. One queue we can be sure exists is CEGUI::RQ_BASE, and so we'll check for that:
Code: Select all
// There are many rendering queues that may (or may not) be present for
// a RenderingSurface. For our purposes, we can hook the 'default' of
// RQ_BASE. - and we need to do our rendering /before/ this queue
if (static_cast<const CEGUI::RenderQueueEventArgs&>(args).queueID != CEGUI::RQ_BASE)
return false;
Once we know we have the right queue, we just update the Ogre::RenderTarget:
Code: Select all
// get the target to draw itself.
mRttTexture->update();
Hmmm. One snag, though
![Wink ;)](./images/smilies/icon_wink.gif)
Doing this obliterates the render states that CEGUI so carefully set up earlier for it's rendering. Which means it's likely that nothing will actually be seen due to this. Because there is no state management in the Ogre::RenderSystem, we have to reinitialise all the states again - this is done via CEGUI::OgreRenderer::initialiseRenderStateSettings - and activate the default rendering root for rendering:
Code: Select all
// Reset the initial CEGUI rendering states.
mRenderer->initialiseRenderStateSettings();
// (Re)Activate the appropriate CEGUI render target.
mRenderer->getDefaultRenderingRoot().getRenderTarget().activate();
With this in place, rendering is now done in the correct order and there is not more flickering or other such atefacts.
2) To solve this second issue we again use an event handler. Basically we subscribe to hear when the window with the CEGUI::RenderingSurface we are targetting is resized:
Code: Select all
// subscribe to hear about resize events. This is needed because
// when the underlying texture gets resized, all the associated objects
// must be assumed to be lost also
fw->subscribeEvent(Window::EventSized,
Event::Subscriber(&OgreRTTDemo::handleResize, this));
In the handler, we see if our pointer to the Ogre::RenderTarget has changed - indicating the old one will no longer be valid - and if it is changed, we update our set up to use the new Ogre::RenederTarget instead. One thing to note here is that the Ogre::Viewport dimensions are relative to the entire surface of the Ogre::RenderTarget, so shrinking the window again does not automatically result in shrinking of the viewport - to overcome this we would adjust the Viewport dimensions based on the CEGUI::Window size and how that relates to the Ogre::RenderTarget size (this was not done in my demo, but just involves fetching and dividing the two dimensions and multiplying the viewport dimensions by the result):
Code: Select all
// get rendering surface used by FrameWindow.
RenderingSurface* surface =
static_cast<const CEGUI::WindowEventArgs&>(args).window->
getRenderingSurface();
// if there's no surface, skip the RTT setup parts!
if (surface)
initialiseRTTViewport(surface);
Here is a video I made of my demo app:
Having gone through all this, I'm not sure how appropriate this is to your scenarion
![Laughing :lol:](./images/smilies/icon_lol.gif)
However, it's a good demonstration of how - with a little effort, at least - it's possible to directly render anything you like to a CEGUI window.
Below are the complete functions that are relevant to the above discussion. I may clean this up into a Wiki article in the coming weeks (if any one else wants to do it instead, that would be great!).
CE
CEGUI Initialisation - called at start up:
Code: Select all
//----------------------------------------------------------------------------//
void OgreRTTDemo::setupCEGUI()
{
using namespace CEGUI;
mRenderer = &OgreRenderer::bootstrapSystem();
// Load scheme and set up defaults.
SchemeManager::getSingleton().create("TaharezLook.scheme");
System::getSingleton().setDefaultMouseCursor("TaharezLook", "MouseArrow");
// create root window.
WindowManager& wmgr(WindowManager::getSingleton());
Window* root = wmgr.createWindow("DefaultWindow");
System::getSingleton().setGUISheet(root);
// create frame window for demo.
Window* fw = wmgr.createWindow("TaharezLook/FrameWindow");
root->addChildWindow(fw);
fw->setSize(UVector2(UDim(0.5f, 0), UDim(0.5f, 0)));
fw->setText("Ogre / CEGUI Direct Rendering Demo");
// create a button
Window* btn = wmgr.createWindow("TaharezLook/Button");
fw->addChildWindow(btn);
btn->setPosition(UVector2(UDim(0.25f, 0), UDim(0.7f, 0)));
btn->setSize(UVector2(UDim(0.5f, 0), UDim(0.15f, 0)));
btn->setText("This is a button!");
// get rendering surface used by FrameWindow.
RenderingSurface* surface = fw->getRenderingSurface();
// if there's no surface, skip the RTT setup parts!
if (surface)
{
initialiseRTTViewport(surface);
// Since we set the Ogre RTT above to not be auto updated, we'll use an
// event subscription to decide when to render that content manually.
// Basically this needs to be done after other UI content is drawn but
// before anything gets put on screen. We need to use an appropriate
// RenderingSurface for this - not the surface used for the window we
// will render directly to - but something higher up. For this demo
// (and many other scenarios) the RenderingRoot will suffice. We use
// the EventRenderQueueStarted because we want to update the content
// /before/ it's drawn to the surface (here that would be the screen).
mRenderer->getDefaultRenderingRoot().subscribeEvent(
RenderingSurface::EventRenderQueueStarted,
Event::Subscriber(&OgreRTTDemo::updateRTTContent, this));
// subscribe to hear about resize events. This is needed because
// when the underlying texture gets resized, all the associated objects
// must be assumed to be lost also
fw->subscribeEvent(Window::EventSized,
Event::Subscriber(&OgreRTTDemo::handleResize, this));
}
}
initialiseRTTViewport Function that initialises the RTT options and viewport:
Code: Select all
//----------------------------------------------------------------------------//
void OgreRTTDemo::initialiseRTTViewport(CEGUI::RenderingSurface* const surface)
{
const Ogre::RenderTexture* const old_rtt = mRttTexture;
// extract the Ogre render target at for the surface
mRttTexture = dynamic_cast<CEGUI::OgreTexture&>(
dynamic_cast<CEGUI::OgreTextureTarget&>(
surface->getRenderTarget()).getTexture()).
getOgreTexture()->getBuffer()->getRenderTarget();
// only do set up if target is changed.
if (old_rtt != mRttTexture)
{
// tell Ogre not to draw this target automatically (else you get
// visible flickering).
mRttTexture->setAutoUpdated(false);
// setup viewport from the same camera as the main scene.
mRttTexture->addViewport(mCamera, 0, 0.02, 0.09, 0.5, 0.5)
->setOverlaysEnabled(false);
}
}
The handler to do the RTT content update:
Code: Select all
//----------------------------------------------------------------------------//
bool OgreRTTDemo::updateRTTContent(const CEGUI::EventArgs& args)
{
// There are many rendering queues that may (or may not) be present for
// a RenderingSurface. For our purposes, we can hook the 'default' of
// RQ_BASE. - and we need to do our rendering /before/ this queue
if (static_cast<const CEGUI::RenderQueueEventArgs&>(args).queueID != CEGUI::RQ_BASE)
return false;
// get the target to draw itself.
mRttTexture->update();
// Reset the initial CEGUI rendering states.
mRenderer->initialiseRenderStateSettings();
// (Re)Activate the appropriate CEGUI render target.
mRenderer->getDefaultRenderingRoot().getRenderTarget().activate();
return true;
}
The handler called when the window is resized:
Code: Select all
//----------------------------------------------------------------------------//
bool OgreRTTDemo::handleResize(const CEGUI::EventArgs& args)
{
using namespace CEGUI;
// get rendering surface used by FrameWindow.
RenderingSurface* surface =
static_cast<const CEGUI::WindowEventArgs&>(args).window->
getRenderingSurface();
// if there's no surface, skip the RTT setup parts!
if (surface)
initialiseRTTViewport(surface);
return true;
}