Difference between revisions of "Using CEGUI with SDL and OpenGL"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
m (Bot: Automated text replacement (-<code>[:space:]?<cpp/>(.*?)</code> +<syntaxhighlight lang="cpp">\1</syntaxhighlight>))
Line 13: Line 13:
 
First SDL:
 
First SDL:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
if (SDL_Init(SDL_INIT_VIDEO)<0)
 
if (SDL_Init(SDL_INIT_VIDEO)<0)
 
{
 
{
Line 19: Line 19:
 
   exit(0);
 
   exit(0);
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
Here we initialise SDL with video support. We need this for CEGUI.
 
Here we initialise SDL with video support. We need this for CEGUI.
 
O.K. now SDL is ready to go. So let's fire up OpenGL:
 
O.K. now SDL is ready to go. So let's fire up OpenGL:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
if (SDL_SetVideoMode(800,600,0,SDL_OPENGL)==NULL)
 
if (SDL_SetVideoMode(800,600,0,SDL_OPENGL)==NULL)
 
{
 
{
Line 31: Line 31:
 
   exit(0);
 
   exit(0);
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
Now OpenGL is ready. But we still need to set a decent configuration:
 
Now OpenGL is ready. But we still need to set a decent configuration:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
glEnable(GL_CULL_FACE);
 
glEnable(GL_CULL_FACE);
 
glDisable(GL_FOG);
 
glDisable(GL_FOG);
 
glClearColor(0.0f,0.0f,0.0f,1.0f);
 
glClearColor(0.0f,0.0f,0.0f,1.0f);
 
glViewport(0,0, 800,600);
 
glViewport(0,0, 800,600);
</code>
+
</syntaxhighlight>
  
 
The OpenGL renderer that comes with CEGUI sets the matrices itself, so if you're using CEGUI for all your rendering needs this would be fine. Normally you would want the normal perspective projection setup though:
 
The OpenGL renderer that comes with CEGUI sets the matrices itself, so if you're using CEGUI for all your rendering needs this would be fine. Normally you would want the normal perspective projection setup though:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
glMatrixMode(GL_PROJECTION);
 
glMatrixMode(GL_PROJECTION);
 
glLoadIdentity();
 
glLoadIdentity();
Line 50: Line 50:
 
glMatrixMode(GL_MODELVIEW);
 
glMatrixMode(GL_MODELVIEW);
 
glLoadIdentity();
 
glLoadIdentity();
</code>
+
</syntaxhighlight>
  
 
SDL and OpenGL are now both ready for action. So it's time to initialise CEGUI.
 
SDL and OpenGL are now both ready for action. So it's time to initialise CEGUI.
 
First we need the renderer.
 
First we need the renderer.
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
#include "renderers/OpenGLGUIRenderer/openglrenderer.h"
 
#include "renderers/OpenGLGUIRenderer/openglrenderer.h"
</code>
+
</syntaxhighlight>
  
 
It must be created before starting CEGUI.
 
It must be created before starting CEGUI.
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
CEGUI::OpenGLRenderer* renderer = new CEGUI::OpenGLRenderer(0,800,600);
 
CEGUI::OpenGLRenderer* renderer = new CEGUI::OpenGLRenderer(0,800,600);
</code>
+
</syntaxhighlight>
  
 
Then the CEGUI::System must be initialised:
 
Then the CEGUI::System must be initialised:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
new CEGUI::System(renderer);
 
new CEGUI::System(renderer);
</code>
+
</syntaxhighlight>
  
 
Remember that you have to load a widget set, set the mouse cursor and a default font before CEGUI is completely ready. This is described in the other tutorials.
 
Remember that you have to load a widget set, set the mouse cursor and a default font before CEGUI is completely ready. This is described in the other tutorials.
Line 76: Line 76:
 
By default the SDL cursor is displayed, so we'll remove that:
 
By default the SDL cursor is displayed, so we'll remove that:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
SDL_ShowCursor(SDL_DISABLE);
 
SDL_ShowCursor(SDL_DISABLE);
</code>
+
</syntaxhighlight>
  
 
As keypress characters needs to be injected into CEGUI, we activate unicode translation for SDL key events:
 
As keypress characters needs to be injected into CEGUI, we activate unicode translation for SDL key events:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
SDL_EnableUNICODE(1);
 
SDL_EnableUNICODE(1);
</code>
+
</syntaxhighlight>
  
 
This makes it alot easier as we don't have to worry about modifier keys and keyboard layouts ourselves. More about this later on...
 
This makes it alot easier as we don't have to worry about modifier keys and keyboard layouts ourselves. More about this later on...
Line 90: Line 90:
 
Key repeat is a nice feature for the text input widgets in CEGUI, so we use SDL to generate them:
 
Key repeat is a nice feature for the text input widgets in CEGUI, so we use SDL to generate them:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
 
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
</code>
+
</syntaxhighlight>
  
 
Everything is ready now, and we can start the main loop :)
 
Everything is ready now, and we can start the main loop :)
Line 99: Line 99:
 
To make it all happen, we use a simple main loop that just keeps pushing on those frames:
 
To make it all happen, we use a simple main loop that just keeps pushing on those frames:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
void main_loop()
 
void main_loop()
 
{
 
{
Line 114: Line 114:
 
   }
 
   }
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
This function will run the main loop until the ''bool'' value ''must_quit'' becomes ''true''. In this tutorial this will happen when the user clicks the close button provided by the window manager.
 
This function will run the main loop until the ''bool'' value ''must_quit'' becomes ''true''. In this tutorial this will happen when the user clicks the close button provided by the window manager.
Line 130: Line 130:
 
Here is what our inject_input function looks like:
 
Here is what our inject_input function looks like:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
void inject_input(bool& must_quit)
 
void inject_input(bool& must_quit)
 
{
 
{
Line 193: Line 193:
  
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
First I'll explain the events that get handled directly in the ''inject_input'' function.
 
First I'll explain the events that get handled directly in the ''inject_input'' function.
Line 200: Line 200:
 
'''Mouse Motion''':
 
'''Mouse Motion''':
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
// we inject the mouse position directly.
 
// we inject the mouse position directly.
 
CEGUI::System::getSingleton().injectMousePosition(
 
CEGUI::System::getSingleton().injectMousePosition(
Line 206: Line 206:
 
   static_cast<float>(e.motion.y)
 
   static_cast<float>(e.motion.y)
 
);
 
);
</code>
+
</syntaxhighlight>
  
 
There is nothing special here. Like stated in the comment the mouse position is just injected directly.
 
There is nothing special here. Like stated in the comment the mouse position is just injected directly.
Line 217: Line 217:
 
This event takes a little more work. CEGUI requires that key characters (the printable character the key represents) are injected alongside key codes.
 
This event takes a little more work. CEGUI requires that key characters (the printable character the key represents) are injected alongside key codes.
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
// to tell CEGUI that a key was pressed, we inject the scancode.
 
// to tell CEGUI that a key was pressed, we inject the scancode.
 
CEGUI::System::getSingleton().injectKeyDown(e.key.keysym.scancode);
 
CEGUI::System::getSingleton().injectKeyDown(e.key.keysym.scancode);
</code>
+
</syntaxhighlight>
  
 
Luckily the key code is just the SDL scancode, so we inject that directly. (This only seems to be true on windows. On other platforms you will need to use a translation function. One can be found here [[SDL to CEGUI keytable]])
 
Luckily the key code is just the SDL scancode, so we inject that directly. (This only seems to be true on windows. On other platforms you will need to use a translation function. One can be found here [[SDL to CEGUI keytable]])
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
// as for the character it's a litte more complicated. we'll use for translated unicode value.
 
// as for the character it's a litte more complicated. we'll use for translated unicode value.
 
// this is described in more detail below.
 
// this is described in more detail below.
Line 231: Line 231:
 
   CEGUI::System::getSingleton().injectChar(e.key.keysym.unicode);
 
   CEGUI::System::getSingleton().injectChar(e.key.keysym.unicode);
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
Instead of formatting the keypress ourselves, we let SDL do it for us. We could check if we actually got a valid ASCII code, but we want support for local characters as well, so we won't do that. For more information, take a look at the SDL documentation for this feature. [http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fkeysym SDL_keysym].
 
Instead of formatting the keypress ourselves, we let SDL do it for us. We could check if we actually got a valid ASCII code, but we want support for local characters as well, so we won't do that. For more information, take a look at the SDL documentation for this feature. [http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fkeysym SDL_keysym].
Line 239: Line 239:
 
This one is simple. Only the keycode need to injected. So we just use the scancode directly (As with KeyDown you will need to use a translation function for non Windows platforms. Check KeyDown above for more info):
 
This one is simple. Only the keycode need to injected. So we just use the scancode directly (As with KeyDown you will need to use a translation function for non Windows platforms. Check KeyDown above for more info):
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
// like before we inject the scancode directly.
 
// like before we inject the scancode directly.
 
CEGUI::System::getSingleton().injectKeyUp(e.key.keysym.scancode);
 
CEGUI::System::getSingleton().injectKeyUp(e.key.keysym.scancode);
</code>
+
</syntaxhighlight>
  
  
Line 248: Line 248:
 
CEGUI and SDL are a little different when it comes to mouse button and mouse wheel events. So a little conversion is necessary. Here's the ''handle_mouse_down'' function that gets called when a mouse button down event occurs in SDL. It takes one parameter, a ''Uint8'' describing the mouse button that was pressed.
 
CEGUI and SDL are a little different when it comes to mouse button and mouse wheel events. So a little conversion is necessary. Here's the ''handle_mouse_down'' function that gets called when a mouse button down event occurs in SDL. It takes one parameter, a ''Uint8'' describing the mouse button that was pressed.
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
void handle_mouse_down(Uint8 button)
 
void handle_mouse_down(Uint8 button)
 
{
 
{
Line 273: Line 273:
 
}
 
}
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
I chose a very "manual" conversion, but it works fine. Everything should be pretty self-explainatory.
 
I chose a very "manual" conversion, but it works fine. Everything should be pretty self-explainatory.
Line 283: Line 283:
 
Like ''handle_mouse_down'' it takes one parameter, a ''Uint8'' describing the mouse button that was released:
 
Like ''handle_mouse_down'' it takes one parameter, a ''Uint8'' describing the mouse button that was released:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
void handle_mouse_up(Uint8 button)
 
void handle_mouse_up(Uint8 button)
 
{
 
{
Line 299: Line 299:
 
}
 
}
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
=== Time Pulses ===
 
=== Time Pulses ===
Line 308: Line 308:
 
CEGUI's interface for injecting time pulses requires that you pass the time in seconds that has passed since the last time pulse injection. Let's take a look at the function:
 
CEGUI's interface for injecting time pulses requires that you pass the time in seconds that has passed since the last time pulse injection. Let's take a look at the function:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
void inject_time_pulse(double& last_time_pulse)
 
void inject_time_pulse(double& last_time_pulse)
 
{
 
{
Line 320: Line 320:
 
last_time_pulse = t;
 
last_time_pulse = t;
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
* The first line gets the actual "run-time" when called.
 
* The first line gets the actual "run-time" when called.
Line 332: Line 332:
 
If the window size changes (e.g. switching to fullscreen mode), we have to tell the openGlRenderer what's the new window size. Otherwise e.g. the Fonts aren't scaled properly. Therefore we add an SDL Event Handler for SDL_VIDEORESIZE and submit the new window size to the openGlRenderer of CEGUI.  Also, on a resize the OpenGL context is lost, meaning textures must be reloaded.  The OpenGLRenderer provides functions to do this, grabTextures and restoreTextures.   
 
If the window size changes (e.g. switching to fullscreen mode), we have to tell the openGlRenderer what's the new window size. Otherwise e.g. the Fonts aren't scaled properly. Therefore we add an SDL Event Handler for SDL_VIDEORESIZE and submit the new window size to the openGlRenderer of CEGUI.  Also, on a resize the OpenGL context is lost, meaning textures must be reloaded.  The OpenGLRenderer provides functions to do this, grabTextures and restoreTextures.   
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
...
 
...
 
       case SDL_VIDEORESIZE:
 
       case SDL_VIDEORESIZE:
Line 342: Line 342:
  
 
...
 
...
</code>
+
</syntaxhighlight>
  
 
=== Rendering ===
 
=== Rendering ===
 
Now all that's left is renderingthe GUI.
 
Now all that's left is renderingthe GUI.
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
void render_gui()
 
void render_gui()
 
{
 
{
Line 359: Line 359:
 
SDL_GL_SwapBuffers();
 
SDL_GL_SwapBuffers();
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
The line:
 
The line:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
CEGUI::System::getSingleton().renderGUI();
 
CEGUI::System::getSingleton().renderGUI();
</code>
+
</syntaxhighlight>
  
 
does all the CEGUI magic and sets OpenGL state itself. As long as the viewport is setup, it will render the GUI.
 
does all the CEGUI magic and sets OpenGL state itself. As long as the viewport is setup, it will render the GUI.
Line 375: Line 375:
 
There are many scenarios where an exception can be thrown. And whether or not these should be considered fatal depends on the application. To make sure you catch the CEGUI exceptions a regular ''try'' block is used. Like so:
 
There are many scenarios where an exception can be thrown. And whether or not these should be considered fatal depends on the application. To make sure you catch the CEGUI exceptions a regular ''try'' block is used. Like so:
  
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
try
 
try
 
{
 
{
Line 385: Line 385:
 
// you could quit here
 
// you could quit here
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
This should provide you with the basic steps needed to get interactive with CEGUI in your SDL application.
 
This should provide you with the basic steps needed to get interactive with CEGUI in your SDL application.
Line 395: Line 395:
  
 
This code uses release 0.5.0 of CEGUI.
 
This code uses release 0.5.0 of CEGUI.
<code><cpp/>
+
<syntaxhighlight lang="cpp">
 
/*
 
/*
 
  * Adapted by: Johnny Souza - johnnysouza.js@gmail.com
 
  * Adapted by: Johnny Souza - johnnysouza.js@gmail.com
Line 568: Line 568:
 
main_loop();
 
main_loop();
 
}
 
}
</code>
+
</syntaxhighlight>
  
  
 
--[[User:Lindquist|Lindquist]] 16:21, 8 May 2005 (BST)
 
--[[User:Lindquist|Lindquist]] 16:21, 8 May 2005 (BST)

Revision as of 12:48, 27 February 2011

Written for CEGUI 0.6


Works with versions 0.6.x (obsolete)

Written for CEGUI 0.5


Works with versions 0.5.x (obsolete)

SDL (Simple DirectMedia Layer) is an excellent library for writing portable games and other multimedia applications, but as it is a low-level library, it has no native support for GUI interfaces.

When using OpenGL for rendering, using CEGUI with SDL is not hard.

I'll assume that you've read the imbiciles tutorials, and have used SDL with OpenGL. And know C / C++ ...


Initialisation

Before we can do anything, we need to initialise our libraries. First SDL:

if (SDL_Init(SDL_INIT_VIDEO)<0)
{
  fprintf(stderr, "Unable to initialise SDL: %s", SDL_GetError());
  exit(0);
}

Here we initialise SDL with video support. We need this for CEGUI. O.K. now SDL is ready to go. So let's fire up OpenGL:

if (SDL_SetVideoMode(800,600,0,SDL_OPENGL)==NULL)
{
  fprintf(stderr, "Unable to set OpenGL videomode: %s", SDL_GetError());
  SDL_Quit();
  exit(0);
}

Now OpenGL is ready. But we still need to set a decent configuration:

glEnable(GL_CULL_FACE);
glDisable(GL_FOG);
glClearColor(0.0f,0.0f,0.0f,1.0f);
glViewport(0,0, 800,600);

The OpenGL renderer that comes with CEGUI sets the matrices itself, so if you're using CEGUI for all your rendering needs this would be fine. Normally you would want the normal perspective projection setup though:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, 800.0/600.0, 0.1,100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

SDL and OpenGL are now both ready for action. So it's time to initialise CEGUI. First we need the renderer.

#include "renderers/OpenGLGUIRenderer/openglrenderer.h"

It must be created before starting CEGUI.

CEGUI::OpenGLRenderer* renderer = new CEGUI::OpenGLRenderer(0,800,600);

Then the CEGUI::System must be initialised:

new CEGUI::System(renderer);

Remember that you have to load a widget set, set the mouse cursor and a default font before CEGUI is completely ready. This is described in the other tutorials.


By default the SDL cursor is displayed, so we'll remove that:

SDL_ShowCursor(SDL_DISABLE);

As keypress characters needs to be injected into CEGUI, we activate unicode translation for SDL key events:

SDL_EnableUNICODE(1);

This makes it alot easier as we don't have to worry about modifier keys and keyboard layouts ourselves. More about this later on...

Key repeat is a nice feature for the text input widgets in CEGUI, so we use SDL to generate them:

SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

Everything is ready now, and we can start the main loop :)

The Main Loop

To make it all happen, we use a simple main loop that just keeps pushing on those frames:

void main_loop()
{
  bool must_quit = false;
 
  // get "run-time" in seconds
  double last_time_pulse = 0.001*static_cast<double>(SDL_GetTicks());
 
  while (!must_quit)
  {
    inject_input(must_quit);
    inject_time_pulse(last_time_pulse);
    render_gui();
  }
}

This function will run the main loop until the bool value must_quit becomes true. In this tutorial this will happen when the user clicks the close button provided by the window manager.

The double value last_time_pulse holds the time of the latest time pulse injection. More about this later.

Each function in the while loop will be described below.

There are endless ways of making your main loop. I took a simple approach to ease writing this tutorial.


Injecting Input and injecting change of window size

When the user press or release keyboard or mouse buttons, we need to tell CEGUI about it, for this we use the injection functions of CEGUI::System. We also have to tell

Here is what our inject_input function looks like:

void inject_input(bool& must_quit)
{
  SDL_Event e;
 
  // go through all available events
  while (SDL_PollEvent(&e))
  {
    // we use a switch to determine the event type
    switch (e.type)
    {
      // mouse motion handler
      case SDL_MOUSEMOTION:
        // we inject the mouse position directly.
        CEGUI::System::getSingleton().injectMousePosition(
          static_cast<float>(e.motion.x),
          static_cast<float>(e.motion.y)
        );
        break;
 
      // mouse down handler
      case SDL_MOUSEBUTTONDOWN:
        // let a special function handle the mouse button down event
        handle_mouse_down(e.button.button);
        break;
 
      // mouse up handler
      case SDL_MOUSEBUTTONUP:
        // let a special function handle the mouse button up event
        handle_mouse_up(e.button.button);
        break;
 
 
      // key down
      case SDL_KEYDOWN:
        // to tell CEGUI that a key was pressed, we inject the scancode.
        CEGUI::System::getSingleton().injectKeyDown(e.key.keysym.scancode);
 
        // as for the character it's a litte more complicated. we'll use for translated unicode value.
        // this is described in more detail below.
        if ((e.key.keysym.unicode != 0)
        {
          CEGUI::System::getSingleton().injectChar(e.key.keysym.unicode);
        }
        break;
 
      // key up
      case SDL_KEYUP:
        // like before we inject the scancode directly.
        CEGUI::System::getSingleton().injectKeyUp(e.key.keysym.scancode);
        break;
 
 
      // WM quit event occured
      case SDL_QUIT:
        must_quit = true;
        break;
 
    }
 
  }
 
}

First I'll explain the events that get handled directly in the inject_input function.


Mouse Motion:

// we inject the mouse position directly.
CEGUI::System::getSingleton().injectMousePosition(
  static_cast<float>(e.motion.x),
  static_cast<float>(e.motion.y)
);

There is nothing special here. Like stated in the comment the mouse position is just injected directly.

There are two ways of injecting mouse motion. One where you inject how much the cursor moved, and one where you inject the mouse cursor position. The last one is failsafe. Then first one only works correctly in fullscreen mode, or with input grabbed. The reason for this is that in regular windowed mode, the mouse can be moved outside the application window, and during this time no mouse motion event are generated. So if we enter the window at another position, the real mousecursor and CEGUI's mouse cursor will be offset, which will break mouse usage.


Key Down
This event takes a little more work. CEGUI requires that key characters (the printable character the key represents) are injected alongside key codes.

// to tell CEGUI that a key was pressed, we inject the scancode.
CEGUI::System::getSingleton().injectKeyDown(e.key.keysym.scancode);

Luckily the key code is just the SDL scancode, so we inject that directly. (This only seems to be true on windows. On other platforms you will need to use a translation function. One can be found here SDL to CEGUI keytable)

// as for the character it's a litte more complicated. we'll use for translated unicode value.
// this is described in more detail below.
if (e.key.keysym.unicode != 0)
{
  CEGUI::System::getSingleton().injectChar(e.key.keysym.unicode);
}

Instead of formatting the keypress ourselves, we let SDL do it for us. We could check if we actually got a valid ASCII code, but we want support for local characters as well, so we won't do that. For more information, take a look at the SDL documentation for this feature. SDL_keysym.


Key Up
This one is simple. Only the keycode need to injected. So we just use the scancode directly (As with KeyDown you will need to use a translation function for non Windows platforms. Check KeyDown above for more info):

// like before we inject the scancode directly.
CEGUI::System::getSingleton().injectKeyUp(e.key.keysym.scancode);


Mouse Button Down and Mouse Wheel
CEGUI and SDL are a little different when it comes to mouse button and mouse wheel events. So a little conversion is necessary. Here's the handle_mouse_down function that gets called when a mouse button down event occurs in SDL. It takes one parameter, a Uint8 describing the mouse button that was pressed.

void handle_mouse_down(Uint8 button)
	{
	switch ( button )
		{
		// handle real mouse buttons
		case SDL_BUTTON_LEFT:
			CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton);
			break;
		case SDL_BUTTON_MIDDLE:
			CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::MiddleButton);
			break;
		case SDL_BUTTON_RIGHT:
			CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton);
			break;
 
		// handle the mouse wheel
		case SDL_BUTTON_WHEELDOWN:
			CEGUI::System::getSingleton().injectMouseWheelChange( -1 );
			break;
		case SDL_BUTTON_WHEELUP:
			CEGUI::System::getSingleton().injectMouseWheelChange( +1 );
			break;
		}
	}

I chose a very "manual" conversion, but it works fine. Everything should be pretty self-explainatory. As you can see mouse wheel events are emitted as mouse button down events in SDL.


Mouse Button Up
The mouse button up event is handled very much like the mouse button down event, except there are no mousewheel release events. Like handle_mouse_down it takes one parameter, a Uint8 describing the mouse button that was released:

void handle_mouse_up(Uint8 button)
	{
	switch ( button )
		{
		case SDL_BUTTON_LEFT:
			CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton);
			break;
		case SDL_BUTTON_MIDDLE:
			CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::MiddleButton);
			break;
		case SDL_BUTTON_RIGHT:
			CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton);
			break;
		}
	}

Time Pulses

SDL has a built-in millisecond counter which we will use for this example. There are other ways to use timers with SDL, but I chose this approach as it is simple to use, and provides decent precision.

Remember in the main loop where we stored the current "run-time" in seconds ? This value will be passed as a reference to inject_time_pulse function which in turn will set a new value to it.

CEGUI's interface for injecting time pulses requires that you pass the time in seconds that has passed since the last time pulse injection. Let's take a look at the function:

void inject_time_pulse(double& last_time_pulse)
{
	// get current "run-time" in seconds
	double t = 0.001*SDL_GetTicks();
 
	// inject the time that passed since the last call 
	CEGUI::System::getSingleton().injectTimePulse( float(t-last_time_pulse) );
 
	// store the new time as the last time
	last_time_pulse = t;
}
  • The first line gets the actual "run-time" when called.
  • The second line injects the time pulse as the difference between the current time and the last time.
  • The third line stores the current time as the last time a time pulse was injected.

This will work for about 47 days... After that the counter wraps to zero and it breaks (a single insanely invalid timepulse will be injected). I'll leave it up to you to fix that if it's a problem.

Change of window size

If the window size changes (e.g. switching to fullscreen mode), we have to tell the openGlRenderer what's the new window size. Otherwise e.g. the Fonts aren't scaled properly. Therefore we add an SDL Event Handler for SDL_VIDEORESIZE and submit the new window size to the openGlRenderer of CEGUI. Also, on a resize the OpenGL context is lost, meaning textures must be reloaded. The OpenGLRenderer provides functions to do this, grabTextures and restoreTextures.

...
      case SDL_VIDEORESIZE:
            renderer->grabTextures();
            //your resize code here, including the SDL_SetVideoMode call
            renderer->restoreTextures();
            renderer->setDisplaySize(CEGUI::Size(e.resize.w, e.resize.h));
            break;
 
...

Rendering

Now all that's left is renderingthe GUI.

void render_gui()
{
	// clear the colour buffer
	glClear( GL_COLOR_BUFFER_BIT );
 
	// render the GUI :)
	CEGUI::System::getSingleton().renderGUI();
 
	// Update the screen
	SDL_GL_SwapBuffers();
}

The line:

CEGUI::System::getSingleton().renderGUI();

does all the CEGUI magic and sets OpenGL state itself. As long as the viewport is setup, it will render the GUI.

Error Handling

The neat C++ architecture of CEGUI suggests that C++ exceptions are used for error handling. This is completely true. Whenever an error occurs, a sub-class of CEGUI::Exception is thrown.

There are many scenarios where an exception can be thrown. And whether or not these should be considered fatal depends on the application. To make sure you catch the CEGUI exceptions a regular try block is used. Like so:

try
{
	// do some cegui code
}
catch (CEGUI::Exception& e)
{
	fprintf(stderr,"CEGUI Exception occured: %s", e.getMessage().c_str());
	// you could quit here
}

This should provide you with the basic steps needed to get interactive with CEGUI in your SDL application. Have fun.

The Code

To compile under linux: gcc teste.cpp -I/usr/include/CEGUI/ -lSDL -lGL -lGLU -lCEGUIBase -lCEGUIOpenGLRenderer

This code uses release 0.5.0 of CEGUI.

/*
 * Adapted by: Johnny Souza - johnnysouza.js@gmail.com
 * Date: 19/01/07 17:00
 * Description: Using CEGUI with SDL and OpenGL
 */
 
#include <stdio.h>
#include <stdlib.h>
 
#include <SDL/SDL.h>
 
#include <CEGUI.h>
/* for release 0.4.X use:
 * #include <renderers/OpenGLGUIRenderer/openglrenderer.h>
 */
#include <RendererModules/OpenGLGUIRenderer/openglrenderer.h>
 
#include <GL/gl.h>
#include <GL/glu.h>
 
 
CEGUI::OpenGLRenderer *renderer;
 
void handle_mouse_down(Uint8 button)
{
	switch ( button ) {
		case SDL_BUTTON_LEFT:
			CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton);
			break;
		case SDL_BUTTON_MIDDLE:
			CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::MiddleButton);
			break;
		case SDL_BUTTON_RIGHT:
			CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton);
			break;
 
		case SDL_BUTTON_WHEELDOWN:
			CEGUI::System::getSingleton().injectMouseWheelChange( -1 );
			break;
		case SDL_BUTTON_WHEELUP:
			CEGUI::System::getSingleton().injectMouseWheelChange( +1 );
			break;
	}
}
 
 
void handle_mouse_up(Uint8 button)
{
	switch ( button )
	{
		case SDL_BUTTON_LEFT:
			CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton);
			break;
		case SDL_BUTTON_MIDDLE:
			CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::MiddleButton);
			break;
		case SDL_BUTTON_RIGHT:
			CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton);
			break;
	}
}
 
 
void inject_input (bool & must_quit) 
{
	SDL_Event e;
	/* go through all available events */
	while (SDL_PollEvent(&e)) {
		/* we use a switch to determine the event type */
		switch (e.type) {
			/* mouse motion handler */
			case SDL_MOUSEMOTION:
				/* we inject the mouse position directly. */
				CEGUI::System::getSingleton().injectMousePosition(static_cast<float>(e.motion.x),static_cast<float>(e.motion.y));
				break;
 
			/* mouse down handler */
			case SDL_MOUSEBUTTONDOWN:
				/* let a special function handle the mouse button down event */
				handle_mouse_down (e.button.button);
				break;
 
			/* mouse up handler */
			case SDL_MOUSEBUTTONUP:
				/* let a special function handle the mouse button up event */
				handle_mouse_up (e.button.button);
				break;
 
			/* key down */
			case SDL_KEYDOWN:
				/* to tell CEGUI that a key was pressed, we inject the scancode. */
				CEGUI::System::getSingleton().injectKeyDown(e.key.keysym.scancode);
				/* as for the character it's a litte more complicated.
				 * we'll use for translated unicode value.
				 * this is described in more detail below.
				 */
				if ((e.key.keysym.unicode & 0xFF80) == 0) {
					CEGUI::System::getSingleton().injectChar(e.key.keysym.unicode & 0x7F);
				}
				break;
 
			/* key up */
			case SDL_KEYUP:
				/* like before we inject the scancode directly. */
				CEGUI::System::getSingleton().injectKeyUp(e.key.keysym.scancode);
				break;
 
			/* WM quit event occured */
			case SDL_QUIT:
				must_quit = true;
				break;
 
			case SDL_VIDEORESIZE:
				renderer->setDisplaySize(CEGUI::Size(e.resize.w, e.resize.h));
				break;
		}
	}
}
 
 
void inject_time_pulse(double& last_time_pulse)
{
	/* get current "run-time" in seconds */
	double t = 0.001*SDL_GetTicks();
	/* inject the time that passed since the last call */
	CEGUI::System::getSingleton().injectTimePulse( float(t-last_time_pulse) );
	/* store the new time as the last time */
	last_time_pulse = t;
}
 
void render_gui()
{
	/* clear the colour buffer */
	glClear( GL_COLOR_BUFFER_BIT );
	/* render the GUI :) */
	CEGUI::System::getSingleton().renderGUI();
	/* Update the screen */
	SDL_GL_SwapBuffers();
}
 
 
void main_loop () 
{
	bool must_quit = false;
	/* get "run-time" in seconds */
	double last_time_pulse = 0.001*static_cast<double>(SDL_GetTicks());
	while (!must_quit) {
		inject_input (must_quit);
		inject_time_pulse (last_time_pulse);
		render_gui ();
	}
}
 
 
int main (int argc, char **argv) 
{
	SDL_Surface * screen;
	atexit (SDL_Quit);
	SDL_Init (SDL_INIT_VIDEO);
	screen = SDL_SetVideoMode (600, 480, 0, SDL_OPENGL);
	if (screen == NULL) {
		/* Se ainda não der, desiste! */ 
		fprintf (stderr, "Impossível ajustar ao vídeo: %s\n", SDL_GetError ());
		exit (1);
	}
	renderer = new CEGUI::OpenGLRenderer (0, 600, 480);
	new CEGUI::System (renderer);
	SDL_ShowCursor (SDL_DISABLE);
	SDL_EnableUNICODE (1);
	SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
	main_loop();
}


--Lindquist 16:21, 8 May 2005 (BST)