Tutorial Introduction
Ogre Tutorial Head

In this tutorial we will be exploring how to use {LEX()}CEGUI{LEX} with Ogre. By the end of this tutorial you should be able to add basic CEGUI functionality to your application. NOTE: This tutorial is not intended to fully teach you how to use CEGUI. This tutorial is intended to get you started. All further CEGUI questions and help should be directed to their home page.

Much deeper and better explained CEGUI tutorials are found in its doxygen documentation with the CEGUI distribution or here on-line

Any problems you encounter during working with this tutorial should be posted in the Help Forum(external link).

Prerequisites

  • This tutorial assumes you have knowledge of C++ programming and are able to set up and compile an Ogre application.
  • This tutorial also assumes that you have created a project using the Ogre Wiki Tutorial Framework, either manually, using CMake or the Ogre AppWizard - see Setting Up An Application for instructions.
  • This tutorial builds on the previous basic tutorials, and it assumes you have already worked through them.

Library versions

  • OGRE: >= 1.7.0
  • CEGUI: >= 0.7.0 and < 0.8.0

NOTE! The API of CEGUI 0.8.x was changed. Most of this tutorial is still valid, but some syntax is different. I have added code where it differs from the 0.7 versions.

You can find the code for this tutorial here. As you go through the tutorial you should be slowly adding code to your own project and watching the results as we build it.

Getting Started

The Initial Code

Modify your Basic Tutorial 7 class header to look like this:

BasicTutorial7 header
#include "BaseApplication.h"

#include <CEGUI/CEGUI.h>
#include <CEGUI/RendererModules/Ogre/CEGUIOgreRenderer.h> //from 0.8 it's just Ogre/Renderer.h

class BasicTutorial7 : public BaseApplication
{
public:
    BasicTutorial7(void);
    virtual ~BasicTutorial7(void);

protected:
    CEGUI::OgreRenderer* mRenderer;

    virtual void createScene(void);

    virtual void createFrameListener(void);

    // Ogre::FrameListener
    virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);

    // OIS::KeyListener
    virtual bool keyPressed( const OIS::KeyEvent &arg );
    virtual bool keyReleased( const OIS::KeyEvent &arg );
    // OIS::MouseListener
    virtual bool mouseMoved( const OIS::MouseEvent &arg );
    virtual bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id );
    virtual bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id );

    bool quit(const CEGUI::EventArgs &e);
};

Make sure that your Basic Tutorial 7 implementation file looks like this:

BasicTutorial7 implementation
#include "BasicTutorial7.h"

//-------------------------------------------------------------------------------------
BasicTutorial7::BasicTutorial7(void)
{
}
//-------------------------------------------------------------------------------------
BasicTutorial7::~BasicTutorial7(void)
{
}
//-------------------------------------------------------------------------------------
void BasicTutorial7::createScene(void)
{
}
//-------------------------------------------------------------------------------------
void BasicTutorial7::createFrameListener(void)
{
    BaseApplication::createFrameListener();
}
//-------------------------------------------------------------------------------------
bool BasicTutorial7::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
    return BaseApplication::frameRenderingQueued(evt);
}
//-------------------------------------------------------------------------------------
bool BasicTutorial7::keyPressed( const OIS::KeyEvent &arg )
{
    return BaseApplication::keyPressed(arg);
}
//-------------------------------------------------------------------------------------
bool BasicTutorial7::keyReleased( const OIS::KeyEvent &arg )
{
    return BaseApplication::keyReleased(arg);
}
//-------------------------------------------------------------------------------------
bool BasicTutorial7::mouseMoved( const OIS::MouseEvent &arg )
{
    return BaseApplication::mouseMoved(arg);
}
//-------------------------------------------------------------------------------------
bool BasicTutorial7::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
    return BaseApplication::mousePressed(arg, id);
}
//-------------------------------------------------------------------------------------
bool BasicTutorial7::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
    return BaseApplication::mouseReleased(arg, id);
}
//-------------------------------------------------------------------------------------
bool BasicTutorial7::quit(const CEGUI::EventArgs &e)
{
    return true;
}




#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
	INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
	int main(int argc, char *argv[])
#endif
	{
		// Create application object
		BasicTutorial7 app;

		try {
			app.go();
		}
		catch (Ogre::Exception& e) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
			MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
			std::cerr << "An exception has occured: " <<
				e.getFullDescription().c_str() << std::endl;
#endif
		}

		return 0;
	}

#ifdef __cplusplus
}
#endif


CEGUI 0.8.x The second include must be changed to

#include <CEGUI/RendererModules/Ogre/Renderer.h>

Project Settings

Windows Settings

You'll need CEGUI, with the Ogre CEGUI renderer compiled against Ogre 1.7 - i.e. the version of Ogre you're using.
See Building CEGUI for instructions.

Then you need to add the location of the CEGUI headers to your project's Include directories. This code assumes that you've prepared a CEGUI SDK with the following directory layout:

cegui sdk dir layout

After compiling CEGUI, you can copy the files over to make an SDK. Doing so is entirely optional, although the instructions here are based on that layout.

If you don't have your CEGUI headers in a directory called 'CEGUI', just remove the 'CEGUI' bit from the include directives in the Basic Tutorial 7 header.

Project Include Directories

Add the following line to your project's list of Include directories (note that this will vary depending on your CEGUI version):

CEGUI_HOME/include

Project Library Directories

CEGUI_HOME/lib

Project Additional Library Dependencies

Debug
CEGUIBase_d.lib
CEGUIOgreRenderer_d.lib
Release
CEGUIBase.lib
CEGUIOgreRenderer.lib


CEGUI 0.8.x

Release
CEGUIBase-0.lib
CEGUIOgreRenderer-0.lib

Move DLLs

On Windows, you need to copy the following DLLs to your OGRE_HOME/bin/debug directory:

CEGUIBase.dll
CEGUIFalagardWRBase.dll
CEGUIOgreRenderer.dll
CEGUIExpatParser.dll

The last DLL assumes that you're using the default Expat XML parser with CEGUI.

CEGUI 0.8.x

CEGUIBase-0.dll
CEGUIOgreRenderer-0.dll
expat.dll
freetype.dll
pcre.dll

The last three dll's can be found in your build of the CEGUI dependencies

Linux Settings

To build this Tutorial under Linux you need to build and install a recent version of CEGUI and then modify you build to include the CEGUI and CEGUI-OGRE libs. How you do this depends on the type of build you're using. If you're using the autotools stub from here: Setting Up An Application - Autotools - Linux then you need to add the following lines to configure.ac

configure.ac
PKG_CHECK_MODULES(CEGUI, [CEGUI >= 0.7])
AC_SUBST(CEGUI_CFLAGS)
AC_SUBST(CEGUI_LIBS)

PKG_CHECK_MODULES(CEGUI_OGRE, [CEGUI-OGRE >= 0.7])
AC_SUBST(CEGUI_OGRE_CFLAGS)
AC_SUBST(CEGUI_OGRE_LIBS)


and change Makefile.ac thus:

Makefile.am
OgreApp_LDADD= $(OGRE_LIBS) $(OIS_LIBS) $(CEGUI_LIBS) $(CEGUI_OGRE_LIBS)


If you use the CMake build then you need to edit CMakeLists.txt to add those libraries.

I found these links useful Setting Up An Application - Autotools - Linux (particularly the adding more libraries section) and Building CEGUI.

Compile the Code

Be sure you can compile and run this code before continuing. The application should do nothing other than present you with a blank screen (press Escape to exit). If you run into compiler or linker errors, check your settings.

A Brief Introduction To CEGUI

CEGUI is a fully featured GUI library that can be embedded in 3D applications such as Ogre (it also supports OpenGL, DirectX, and Irrlicht backends). Much in the same way that Ogre is only a graphics library (and doesn't do other things such as sound, physics, etc), CEGUI is only a GUI library, meaning it does not do its own rendering nor does it hook into any mouse or keyboard events. In fact, in order for CEGUI to render at all, you have to provide a renderer for it (which is the CEGUIOgreRenderer library shipped with CEGUI), and in order for it to even understand mouse and keyboard events you have to manually inject them into the system. This may seem like a pain at first, but in reality very little code is required to make this happen. It also allows you to have full control over the rendering and the input; CEGUI will never get in the way.

There are many aspects to CEGUI and many quirks that will be unfamiliar to you (even if you have used GUI systems before). I will try to slowly introduce them to you as we go along.

Integrating with Ogre

Defining CEGUI resource groups

CEGUI like Ogre needs diverse types of resources to function. It has several resource managers (like Ogre) which need to find their respective resource locations, so you need to define the necessary resource groups and their locations within resources.cfg.

CEGUI'S resources can be found in different locations depending on your platform. For Linux versions, they usually all get installed into /usr/local/share/CEGUI or /usr/share/CEGUI.

Add the following to resources.cfg - replace 'path_to_cegui' with the path to the CEGUI datafiles:

[Imagesets]
FileSystem=path_to_cegui/imagesets
[Fonts]
FileSystem=path_to_cegui/fonts
[Schemes]
FileSystem=path_to_cegui/schemes
[LookNFeel]
FileSystem=path_to_cegui/looknfeel
[Layouts]
FileSystem=path_to_cegui/layouts

Note that, as is mentioned above, on Ubuntu 10.04 LTS for instance, the path specification "FileSystem=/usr/local/share/CEGUI/xml_schemas" must be added to the [General] section of resources.cfg in order to load the GUI scheme.

Initializing CEGUI

Find the createScene function and add the following code:

mRenderer = &CEGUI::OgreRenderer::bootstrapSystem();

Now that CEGUI has been initialized, we need to set the so-called default resource groups for each of CEGUI'S resource managers.
Add the following:

CEGUI::Imageset::setDefaultResourceGroup("Imagesets");
CEGUI::Font::setDefaultResourceGroup("Fonts");
CEGUI::Scheme::setDefaultResourceGroup("Schemes");
CEGUI::WidgetLookManager::setDefaultResourceGroup("LookNFeel");
CEGUI::WindowManager::setDefaultResourceGroup("Layouts");


CEGUI 0.8.x The first line (Imageset::...) changes to

CEGUI::ImageManager::setImagesetDefaultResourceGroup("Imagesets");


As you can see, we used the resource groups we defined in resources.cfg.

CEGUI is highly customizable, and allows you to define the look and feel of your application by changing its skin (scheme, in terms of CEGUI). We will not be covering how to skin the library in any tutorial, so if you wish to learn more about it, consult the CEGUI website.

The following line of code selects the skin:

CEGUI::SchemeManager::getSingleton().create("TaharezLook.scheme");

The next thing we need to do is set the default mouse cursor:

CEGUI::System::getSingleton().setDefaultMouseCursor("TaharezLook", "MouseArrow");

The first parameter specifies the Imageset and the second one specifies the name of the Image to use from that Imageset.

CEGUI 0.8.x The syntax for setting the image for the mouse is a bit different in 0.8.x:

CEGUI::SchemeManager::getSingleton().createFromFile("TaharezLook.scheme");
CEGUI::System::getSingleton().getDefaultGUIContext().getMouseCursor().setDefaultImage("TaharezLook/MouseArrow");


Throughout this tutorial series we will be using CEGUI to display the mouse cursor, even when we have no other use for the GUI library. It is possible to use another GUI library to render the mouse, or to simply create your own mouse cursor using Ogre directly (though this latter option can be a bit involved). If you are only using CEGUI for the mouse cursor and are concerned about memory usage or the disk space that your game takes up, you can look into one of these options to replace CEGUI.

Lastly note that in that last code snippet we have set the default mouse cursor, but we did not set the mouse cursor directly using the MouseCursor::setImage function as we will in later tutorials. This is because in this tutorial we will always be over some kind of CEGUI window (though it may be invisible), so setting the default cursor will, in effect, make the mouse cursor be the image we selected. If we set the mouse cursor directly and did not set the default, the mouse cursor would be invisible every time it passed over a CEGUI window (which, in this tutorial, will be all the time). On the other hand, setting the default mouse image does nothing if you do not have any CEGUI windows displayed, as will be the case in later tutorials. In that situation, calling MouseCursor::setImage() will display the cursor for the application. Example:

// Do not add this to the program
CEGUI::MouseCursor::getSingleton().setImage( CEGUI::System::getSingleton().getDefaultMouseCursor());

Removing SDKTrays

Before moving on, we need to remove the OgreBites SDKTrays from our application. We do that by overriding two functions: createFrameListener() and frameRenderingQueued().

createFrameListener

Copy the contents of the BaseApplication::createFrameListener function to BasicTutorial7::createFrameListener and delete the bit where SDKTrays is created and the debug overlay set up.
It should look like this when we're done getting rid of it:

void BasicTutorial7::createFrameListener(void)
{
    Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
    OIS::ParamList pl;
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;

    mWindow->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

    mInputManager = OIS::InputManager::createInputSystem( pl );

    mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
    mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));

    mMouse->setEventCallback(this);
    mKeyboard->setEventCallback(this);

    //Set initial mouse clipping size
    windowResized(mWindow);

    //Register as a Window listener
    Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);

    mRoot->addFrameListener(this);
}

frameRenderingQueued

We also need to override BaseApplication::frameRenderingQueued.
Make it look like this:

bool BasicTutorial7::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
    if(mWindow->isClosed())
        return false;

    if(mShutDown)
        return false;

    //Need to capture/update each device
    mKeyboard->capture();
    mMouse->capture();

    //Need to inject timestamps to CEGUI System.
    CEGUI::System::getSingleton().injectTimePulse(evt.timeSinceLastFrame);

    return true;
}

We just removed the mTrayMgr and the mDetailsPanel parts and let CEGUI System know the passing of time.

Injecting Key Events

CEGUI does not handle input in any way. It does not read mouse movements or keyboard input. Instead it relies on the user to inject key and mouse events into the system. The next thing we will need to do is to handle the key events.
If you are working with CEGUI, you will need to have the mouse and keyboard in buffered mode so you can receive the events directly and inject them as they happen.
Find the keyPressed function and add the following code to it (replacing what is there):

CEGUI::System &sys = CEGUI::System::getSingleton();
sys.injectKeyDown(arg.key);
sys.injectChar(arg.text);
return true;


CEGUI 0.8.x The conversion between OIS::Keyevent and CEGUI::Key::Scan is not very clean here. Maybe there is a nicer way, but I don't know it.

CEGUI::GUIContext& context = CEGUI::System::getSingleton().getDefaultGUIContext();
context.injectKeyDown((CEGUI::Key::Scan)arg.key);
context.injectChar((CEGUI::Key::Scan)arg.text);
return true;


After getting the system object, we need to do two things.
The first is to inject the key down event into CEGUI.
The second is to inject the actual character that was pressed.
It is very important to inject the character properly since injecting the key down will not always bring about the desired result when using a non-English keyboard. The injectChar was designed with Unicode support in mind.

Now we need to inject the key up event into the system.
Find the keyReleased function and add the following code (replacing what is there):

CEGUI::System::getSingleton().injectKeyUp(arg.key);
return true;


CEGUI 0.8.x

CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyUp((CEGUI::Key::Scan)arg.key);
return true;


Note that we do not need to inject a character up event, only the key up event is required.

Converting and Injecting Mouse Events

Now that we have finished dealing with keyboard input, we need to take care of mouse input.
We have a small issue that we will need to address, however.
When we injected the key up and down events into CEGUI we never had to convert the key. Both OIS and CEGUI use the same key codes for keyboard input. The same is not true for mouse buttons.
Before we can inject mouse button presses into CEGUI, we will need to write a function which converts OIS button IDs into CEGUI button IDs.
Add the following code to BasicTutorial7.cpp:

CEGUI::MouseButton convertButton(OIS::MouseButtonID buttonID)
{
    switch (buttonID)
    {
    case OIS::MB_Left:
        return CEGUI::LeftButton;

    case OIS::MB_Right:
        return CEGUI::RightButton;

    case OIS::MB_Middle:
        return CEGUI::MiddleButton;

    default:
        return CEGUI::LeftButton;
    }
}

It can stay a local static function, so no need to declare it in the header.

NOTE:
For this to compile properly the function must be declared/defined prior to it's invocation. Be sure to add it to the TOP of the BasicTutorial7.cpp file after the includes.

Now we are ready to inject mouse events.
Find the mousePressed function and add the following code (replacing what is there):

CEGUI::System::getSingleton().injectMouseButtonDown(convertButton(id));
return true;


CEGUI 0.8.x Simply follow the same structure as for the key presses, so add .getDefaultGUIContext() . Also for the next couple of statements.

This should be roughly self-explanatory.
We convert the button ID which was passed in, and pass the result to CEGUI.

Find the mouseReleased function and add this line of code (replacing what is there):

CEGUI::System::getSingleton().injectMouseButtonUp(convertButton(id));
return true;

Lastly, we need to inject mouse motion into CEGUI.
The CEGUI::System object has an injectMouseMove function which expects relative mouse movements.
The OIS::mouseMoved handler gives us those relative movements in the state.X.rel variable and the state.Y.rel variables.
Find the mouseMoved function and add the following code (replacing what is there):

CEGUI::System &sys = CEGUI::System::getSingleton();
sys.injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
// Scroll wheel.
if (arg.state.Z.rel)
    sys.injectMouseWheelChange(arg.state.Z.rel / 120.0f);
return true;

120 is a sort of 'magic number' that was used by Microsoft back in the day, and it's very common in modern systems. OIS uses the same magic number; others, like GLUT, do not. Google it to find out more.

That's it. Now CEGUI is fully set up and receiving mouse and keyboard events. Running it now will already display the custom mouse cursor, but to quit you'll have to press Alt+F4.
Note:
In 0.8.3 version, you can't see the custom mouse cursor until you create and display a sheet (as following).

CEGUI::WindowManager &wmgr = CEGUI::WindowManager::getSingleton();
CEGUI::Window *sheet = wmgr.createWindow("DefaultWindow", "CEGUIDemo/Sheet");
CEGUI::System::getSingleton().getDefaultGUIContext().setRootWindow(sheet);

And another problem, you must move your mouse a bit once, before the cursor becomes visible. Currently, I have no idea about the reason. Glad to know if someone could tell me.

Windows, Sheets, and Widgets

Introduction

CEGUI is very different from most GUI systems. In CEGUI, everything that is displayed is a subclass of the CEGUI::Window class, and a window can have any number of child windows. This means that when you create a frame to contain multiple buttons, that frame is a Window.

This can cause some strange things to happen. You can place a button inside another button, though that would really never happen in practice.

The reason that I mention all of this is when you are looking for a particular widget that you have placed in the application, you need to know they are all called Windows, and are accessed by functions that refer to them as such.

In most practical uses of CEGUI, you will not create each individual object through code. Instead, you create a GUI layout for your application in an editor such as the CEGUI Layout Editor. After placing all of your windows, buttons, and other widgets onto the screen as you like them, the editor saves the layout as a text file. You can later load this layout into what CEGUI calls a GUI sheet (which is also a subclass of CEGUI::Window).

Lastly, know that CEGUI contains a large number of widgets that you can use in your application.
We will not cover them in this tutorial, so if you decide to use CEGUI, be sure to take a good look at their website for more information.

Loading a Sheet

In CEGUI loading a sheet is very easy to do. The WindowManager class provides a "loadWindowLayout" function which loads the sheet and puts it into a CEGUI::Window object.
Then you call CEGUI::System::setGUISheet to display it.
We will not be using this in this tutorial, but I would feel remiss if I did not at least show you an example of its use.

Do not add this to the tutorial (or if you do, remove it after you have seen the results):

// Do not add this to the program
CEGUI::Window *guiRoot = CEGUI::WindowManager::getSingleton().loadWindowLayout("TextDemo.layout"); 
CEGUI::System::getSingleton().setGUISheet(guiRoot);

CEGUI 0.8.x

// Do not add this to the program
CEGUI::Window *guiRoot = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("TextDemo.layout"); 
CEGUI::System::getSingleton().getDefaultGUIContext().setRootWindow(guiRoot);

This sets the sheet currently being displayed.
You can later retrieve this sheet by calling System::getGUISheet.
You can also swap the GUI sheet seamlessly by calling setGUISheet with whatever sheet you want to swap to (though be sure to hold onto a pointer to the current sheet if you wish to swap it back).

Manually creating an Object

As I said before, most of the time you use CEGUI, you will be using GUI sheets that you create using an editor. Occasionally, however, you will need to manually create a widget to put on the screen. In this example, we will be adding a Quit button which we will later add functionality to. Since we will have added more than just the Quit button to the screen by the time the tutorial is over, we need to first create a default CEGUI::Window which will contain all of the widgets we will be creating.

Add this to the end of the createScene function:

CEGUI::WindowManager &wmgr = CEGUI::WindowManager::getSingleton();
CEGUI::Window *sheet = wmgr.createWindow("DefaultWindow", "CEGUIDemo/Sheet");

This uses the WindowManager to create a "DefaultWindow" called "CEGUIDemo/Sheet".
While we could name the window anything we like, it's very common (and encouraged) to name the widget in a hierarchical manner such as "SomeApp/MainMenu/Submenu3/CancelButton".

The next thing we need to do is create the Quit button and set its size:

CEGUI::Window *quit = wmgr.createWindow("TaharezLook/Button", "CEGUIDemo/QuitButton");
quit->setText("Quit");
quit->setSize(CEGUI::UVector2(CEGUI::UDim(0.15, 0), CEGUI::UDim(0.05, 0)));


CEGUI 0.8.x Instead of UVector2, you must use USize

This is very close to being cryptic.

CEGUI uses a "unified dimension" system for its sizes and positions. When setting the size you must create a UDim object to tell it what size it should be.

The first parameter is the relative size of the object in relation to its parent.

The second parameter is the absolute size of the object (in pixels).

The important thing to realize is that you're only supposed to set one of the two parameters of a UDim object; the other parameter must be 0. In this case we have made a button which is 15% as wide as its parent and 5% as tall. If we wanted to specify that it should be 20 pixels by 5 pixels, we would set the second parameter in both of the UDim calls to be 20 and 5 respectively, and the first parameter to be 0.

The last thing we have to do is attach the Quit button to the sheet we have created, and then set the current GUI sheet for the system to be that sheet:

sheet->addChildWindow(quit);
CEGUI::System::getSingleton().setGUISheet(sheet);


CEGUI 0.8.x

sheet->addChild(quit);
CEGUI::System::getSingleton().getDefaultGUIContext().setRootWindow(sheet);


Now if you compile and run your application you will see a Quit button in the top left hand corner of the screen, but it does not yet do anything when you click on it. You can use Alt+F4 to close the window.

Events

Events in CEGUI are very flexible.
Instead of using an interface that you implement to receive events, it uses a callback mechanism which binds any public function (with the appropriate method signature) to be the event handler.
Unfortunately this also means that registering events is a bit more complicated.
We will now register to handle the Quit button's click event to exit the program when it is pressed.
To do that, we will first need a pointer to the Quit button we created in the previous section.

Add the following code to BasicTutorial7::createScene after you have created your quit button window:

quit->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&BasicTutorial7::quit, this));

This will subscribe to the clicked event.
Every widget in CEGUI has a set of events that it supports, and they all begin with "Event".
The first parameter to subscribeEvent is the event itself.
The second parameter is an Event::Subscriber object.
When creating a Subscriber object, the first thing we pass in is a pointer to the function that will handle the event (note the & symbol which gives us the pointer to the function).
The second thing we pass to the Subscriber object is the BasicTutorial7 object which will handle the event (which is the "this" object).
That's it!
Our BasicTutorial7::quit function (which has already been defined) will handle the mouse click and terminate the program.

Add the following code to BasicTutorial7::quit (replacing the contents):

mShutDown = true;
return true;


Compile and run your application to test this out.

One thing to note is that we can create any number of functions to handle events for CEGUI.
The only restrictions on them are they must return a bool, and they must take in a single parameter of type "const CEGUI::EventArgs &".
For more information about events (and how to unsubscribe from them), be sure to read more on the CEGUI website.

Render to Texture


One of the more interesting things we can do with CEGUI is create a render-to-texture window. This allows us to create a second Viewport that can be rendered directly into a CEGUI widget. To do this, we need to start by setting up a scene to look at.

Add the following code to the bottom of the createScene function:

mSceneMgr->setAmbientLight(Ogre::ColourValue(1, 1, 1));
mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(0, 0, -300));
headNode->attachObject(ogreHead);


Now we must create the RenderTexture.
The RenderSystem object provides the functionality to render to a texture.
To do this we create a texture with the TextureManager::createManual function.

For this program we will create a 512 x 512 texture:

Ogre::TexturePtr tex = mRoot->getTextureManager()->createManual(
    "RTT",
    Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
    Ogre::TEX_TYPE_2D,
    512,
    512,
    0,
    Ogre::PF_R8G8B8,
    Ogre::TU_RENDERTARGET);
Ogre::RenderTexture *rtex = tex->getBuffer()->getRenderTarget();

See the API reference for more information on this function.

Next we need to create a Camera and a Viewport to look at the scene we have created.
Note that we have changed a couple of Viewport options, including turning off Overlays...which is very important to do or you will get CEGUI and Ogre overlays within our mini-window.

Ogre::Camera *cam = mSceneMgr->createCamera("RTTCam");
cam->setPosition(100, -100, -400);
cam->lookAt(0, 0, -300);
Ogre::Viewport *v = rtex->addViewport(cam);
v->setOverlaysEnabled(false);
v->setClearEveryFrame(true);
v->setBackgroundColour(Ogre::ColourValue::Black);

Note that we have added the Viewport to the texture itself (as opposed to the RenderWindow, which is where we usually add Viewports).

Now that we have created our scene and our texture, we need to embed it within CEGUI.
You can create a CEGUI::Texture from any Ogre texture by calling the CEGUI::OgreRenderer::createTexture function:

CEGUI::Texture &guiTex = mRenderer->createTexture(tex);

CEGUI 0.8.x

CEGUI::Texture &guiTex = mRenderer->createTexture("textname", tex);

Unfortunately, this is where things get complicated.
In CEGUI you never just deal with a single Texture or a single image.
CEGUI works with image sets instead of individual images.
It is very useful to work with entire grids of images when you are trying to define the look and feel of a skin you are creating (for example, take a look at TaharezLook.tga in the datafiles/imagesets folder of the CEGUI SDK to see what an image set looks like).
However, even when you are only trying to define a single image, you must create an entire image set for it.



This is what we will be doing:

CEGUI::Imageset &imageSet =
  CEGUI::ImagesetManager::getSingleton().create("RTTImageset", guiTex);
imageSet.defineImage("RTTImage",
                     CEGUI::Point(0.0f, 0.0f),
                     CEGUI::Size(guiTex.getSize().d_width,
                                 guiTex.getSize().d_height),
                     CEGUI::Point(0.0f, 0.0f));

The first line creates the image set (called "RTTImageset") from the texture that we have provided it.
The next line (which calls defineImage), specifies that the first and only image is called "RTTImage" and it is as large as the entire guiTex texture we have provided.

CEGUI 0.8.x The ideas are the same, but the syntax is different. Instead of creating an image set, we now just create an image by using the image manager. The image manager will remember the name "RTTImage".

const CEGUI::Rectf rect(CEGUI::Vector2f(0.0f, 0.0f), guiTex.getOriginalDataSize());
CEGUI::BasicImage* image = (CEGUI::BasicImage*)( &CEGUI::ImageManager::getSingleton().create("BasicImage", "RTTImage"));
   image->setTexture(&guiTex);
   image->setArea(rect);
   image->setAutoScaled(CEGUI::ASM_Both);


Finally we need to create the StaticImage widget which will house the render texture.
The first part is no different from creating any other window:

CEGUI::Window *si = CEGUI::WindowManager::getSingleton().createWindow("TaharezLook/StaticImage", "RTTWindow");
//si->setSize(CEGUI::UVector2(CEGUI::UDim(0.5f, 0),
si->setSize(CEGUI::USize(CEGUI::UDim(0.5f, 0),
                            CEGUI::UDim(0.4f, 0)));
si->setPosition(CEGUI::UVector2(CEGUI::UDim(0.5f, 0),
                                CEGUI::UDim(0.0f, 0)));

Now we need to specify which image this StaticImage widget will display. Once again, since CEGUI always deals with image sets and not individual images, we must now retrieve the exact image name from the image set, and display it:

si->setProperty("Image", CEGUI::PropertyHelper::imageToString(&imageSet.getImage("RTTImage")));

If it seems like we have packed a texture into an image set only to unpack it again, it's because that's exactly what we have done.
Manipulating images in CEGUI is not one of the easiest or most straightforward things in the library.

CEGUI 0.8.x makes it a bit easier

si->setProperty("Image", "RTTImage");


The last thing we need to do is add the StaticImage widget to the GUI sheet we created earlier:

sheet->addChildWindow(si);


CEGUI 0.8.x

sheet->addChild(si);


Now we are finished. Compile and run the application. :-)

Image

Conclusion

Now you should have a basic understanding of how to create a simple GUI in Ogre using CEGUI.

Full Source

If you are having difficulty building this tutorial, take a look at the source code for it and compare it to your project.

Alternatives

Here are a few alternatives to CEGUI:

More Information

There are also several other places you can get more information about {LEX()}CEGUI{LEX}.

Next

Proceed to Basic Tutorial 8 Using Multiple SceneManagers


Alias: Basic_Tutorial_7

<HR>
Creative Commons Copyright -- Some rights reserved.


THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

  • "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
  • "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
  • "Licensor" means the individual or entity that offers the Work under the terms of this License.
  • "Original Author" means the individual or entity who created the Work.
  • "Work" means the copyrightable work of authorship offered under the terms of this License.
  • "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
  • "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

  • to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
  • to create and reproduce Derivative Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
  • For the avoidance of doubt, where the work is a musical composition:
    • Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
    • Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
    • Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).


The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.

4. Restrictions

The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

  • You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
  • You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
  • If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability.

EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

  • This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
  • Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

  • Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
  • Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
  • If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
  • No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
  • This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.