Practical Application - Something With A Bit More Meat         In this article we are going to integrate CEGUI into the Practical Application codebase

In this article we are going to integrate CEGUI into the Practical Application codebase.

CEGUI


This version of the code will also say goodbye to the VC71 projects and hello to the VC8 (Visual C++ 2005) projects, since that's what I work in now (and have done for some time now). Those using VC71 probably have enough experience to be able to cobble together the projects to build the code — it's fairly straightforward. This version of the code also targets Ogre 1.4.x (Eihort) — again, if you are running Dagon (1.2.x) you can probably figure out the couple of minor changes needed to make it run; they are primarily the way that the message pump is exercised, as well as some OIS changes (since the Eihort samples use the latest OIS, and that's what comes with the SDK). As always, this code was built and tested against the Ogre SDK (1.4.1 in this case).

CEGUI is a 2D UI rendering library that is rendering-engine agnostic — it has direct support for OpenGL and Irrlicht as well as Ogre. It is a library that is heavily XML-based in terms of its script file formats. While you can certainly define all of your UI in code, it is simpler to define it in XML files that CEGUI loads and parses at runtime. CEGUI comes with several samples, including the media needed to run those samples and the XML script files needed to define the look and feel of the UI elements used in the samples. These provided schemes, look-and-feel, and layout files are good to get you started, but chances are you will want to define your own UI widget look and feel for your own application. However, doing that is beyond the scope of this article; see the CEGUI site for more.

Rendering The UI


CEGUI renders its UI elements to mesh quads that are drawn to the display in "screen space". This means that the mesh objects that make up the UI elements on your screen are drawn without any view or projection transformations. This is the standard way that any 2D UI system operates. CEGUI interacts with Ogre via the "OgreGUIRenderer", a class that is provided in the Ogre "Samples" codebase but exists in the "CEGUI" namespace. The OgreGUIRenderer simply takes draw commands from CEGUI and translates them into Ogre Mesh and texture data that Ogre can then display, usually in the "Overlay" render queue (RENDER_QUEUE_OVERLAY). This ensures that the UI is drawn on top of everything else in your scene. In code, you do not need to deal with the OgreGUIRenderer class other than to make an instance of one and pass it to CEGUI, which then takes it from there.

Input and CEGUI


You have to provide CEGUI with all input events — it does not have any inherent input processing of its own, depending instead on input data provided by your application. This has a few nice benefits. For one, you don't have to worry about the UI library fighting with your application for access to the input data. Furthermore, you can cut CEGUI entirely out of the "input loop" at your discretion simply by not providing it with any input data. You also are not tied to any particular input library or API; you can use whatever you like: OIS, SDL, DirectInput, Win32 or XWindows input message data, it's entirely up to you. In this article we are using OIS to gather the input events and forward those to CEGUI.

CEGUI Data Files


As mentioned, you are perfectly able to define your UI layouts entirely in code, but the method most easily iterated is via the several XML files CEGUI can consume as input data. These files define the "scheme", "look-n-feel", "layout", "imagesets" and fonts that CEGUI displays.

  • Scheme. Definition of the different UI elements that are valid in a particular "scheme", for example buttons, listboxes, and so on. Found in .scheme files.
  • Look-And-Feel. Definition of the way that each UI element is presented on the display, including its behaviors and the textures used to render it. Found in .looknfeel files.
  • Layout. Defines the position, size, parenting hierarchy and other properties used to display actual UI elements in a single unit: the UI "sheet". Found in .layout files.
  • Imageset. Defines the textures used in a scheme, and the UV coordinates that are actually mapped to UI element quads on your screen. Found in .imageset files.
  • Font. Should be obvious; CEGUI needs to know where to find the fonts you intend to use for your text, including the glyph definitions and the font texture to use. Found in .font files.


In the gui.zip resource data file accompanying the source for this article, you will find many examples of all of these types of files. Look through each type of file and see what they contain — they are all just text XML files. Their format and contents should be self-evident once you've examined them for a bit — if not, the CEGUI site has documentation on them all. This article uses the "TaharezLookSkin" scheme. (The name "Taharez" comes from the screen name of the person who created the scheme, look-and-feel, texture and imageset for the scheme).

Falagard Skinning System


There is also another part of CEGUI that works behind the scenes, but starting with CEGUI 0.5.x (the version used in this article), is core to its operation. The "Falagard" skinning system was devised as a way to remove the need to create a separate code module (DLL) that was used actually to assemble and render each different scheme and look-and-feel. As you might expect (if you are a regular in the Ogre forums) the person (at least mostly) responsible for its creation was Falagard (unless my information is incorrect, of course). This method of UI skinning is a generalized, data-driven way to render the UI elements without having to author special code to do it. It relies on all of that data found in the look-and-feel and scheme files for its operation — verbosity of data is one of the prices you pay for flexibility. ;)

The Code


So, enough introduction, let's look at the code. If you have followed the series this far, you are familiar with the code that produces a blank Ogre render window and waits for you to press ESC to quit. In this version of the code, the blank window is replaced by a layout that we used in our proof-of-concept phase as a sort of minimalist main menu page. It contains three buttons, only one of which does anything. What that one button (the "Quit" button) does is allow you, in addition to the ESC key, the ability to click that button to exit the app. While that may sound underwhelming, it's purpose is served in that it demonstrates the entire data pathway needed to get input events into CEGUI (in this case, mouse click) through the actual invocation of your code in response to that click. From there it should be fairly obvious how you would go about hooking CEGUI actions into your own code. Note that this is all in C++ — if you are interested in script hooks into CEGUI (such as for Lua or other scripting engines) you are encouraged to visit the CEGUI site for more on how to do that.

The Data


At the risk of making myself a liar right off the bat, we first should look at the vital CEGUI data file used in this article: the layout file. There is more than one visual CEGUI layout editing tool available, and each is in a different state of completeness and maintenance, so you might find it simplest just to edit the XML .layout files by hand — I did. YMMV.

In the file gui.zip in the resource/ directory under Debug in the source distribution, you will find a file called "katana.layout". Here is the contents of that file in its entirety:

<?xml version="1.0" ?>
<GUILayout>
	<Window Type="TaharezLook/FrameWindow" Name="Main">
            <Property Name="UnifiedAreaRect" Value="{{0.0,0},{0.0,0},{1.0,0},{1.0,0}}" />
            <Property Name="FrameEnabled" Value="false" />
            <Property Name="TitlebarEnabled" Value="false" />
            <Property Name="CloseButtonEnabled" Value="False" />

		<Window Type="TaharezLook/Button" Name="cmdQuit">
			<Property Name="Text" Value="Quit" />
       		<Property Name="UnifiedAreaRect" Value="{{0.4,0},{0.7,0},{0.6,0},{0.77,0}}" />
		</Window>
		<Window Type="TaharezLook/Button" Name="cmdOptions">
       		<Property Name="UnifiedAreaRect" Value="{{0.4,0},{0.6,0},{0.6,0},{0.67,0}}" />
			<Property Name="Text" Value="Options" />
		</Window>
		<Window Type="TaharezLook/Button" Name="cmdInstantAction">
       		<Property Name="UnifiedAreaRect" Value="{{0.4,0},{0.5,0},{0.6,0},{0.57,0}}" />
			<Property Name="Text" Value="Instant Action" />
		</Window>
	</Window>
</GUILayout>


The first thing you notice (probably), at least when your eyes stop spinning, are those weird sets of numbers in the braces. As of CEGUI 0.5.x, the only way to define UI element dimensions is with the "Unified Dimension" (UDim) system. In this layout file, we are using UnifiedAreaRect, which is a series of number pairs that define the left, top, right and bottom positions of your UI element. The pairs of numbers are set up to allow you to define your dimensions in either relative or absolute terms, in the space of the containing element. For example, the rect definition

{{0.4,0},{0.5,0},{0.6,0},{0.57,0}}

tells CEGUI that, in the "space" of the the parent element (in this case, the FrameWindow which makes up the top-level container for the entire GUI sheet), we want to place our element (in the case of this set of numbers that I stole from the layout, the "Instant Action" button) 40% (0.4) from the left of the FrameWindow, and 50% (0.5) from the top (using the upper-left corner of the button as its origin). The button's width will be 20% (0.2, or "0.6 - 0.4") of the parent's width and its height will be 7% of the parent's height (0.07, or "0.57 - 0.5"). So, what are those other zeros in there? If we had wanted to define the button's size in "absolute" terms, we would have used those numbers instead (and inserted actual pixel values instead of relative coordinates). You can also combine relative and absolute values in any way you like; I don't recall the order of precedence if you specify both for a particular dimension — it's been a while since I put together this layout, and anyone who knows for sure can feel free to edit this article. ;) BTW, anyone who has done any web page layout should be VERY familiar with this concept of relative and absolute dimensions — it's the same thing.

The first lines in the layout tell CEGUI what we want to call our top-level window ("Main"), and that we want it to take up the whole screen — (0,0) to (1,1) in relative terms. We do not want to see its title bar (which is on by default), it's "X" close button (ditto) or its frame (ditto again). We place three buttons on the screen, in the center, organized top to bottom (but not in the order they appear in the file — when you run the app they will display "Instant Action", then "Options", then "Quit" from top to bottom). The buttons' "Name" parameter is important — this is how we will hook the button actions (mouse click for example) into code, as we will see shortly.

And Then, Now The Code


I will highlight now the changes made to the code to integrate CEGUI into the application. First, in main.cpp, some additional headers:

// needed to be able to create the CEGUI renderer interface
#include "OgreCEGUIRenderer.h"

// CEGUI includes
#include "CEGUISystem.h"
#include "CEGUIInputEvent.h"
#include "CEGUIWindow.h"
#include "CEGUIWindowManager.h"
#include "CEGUISchemeManager.h"
#include "CEGUIFontManager.h"
#include "elements/CEGUIFrameWindow.h"


CEGUI 0.7.1 (really 0.7.0) introduced several changes that, in my opinon, clean up the look of the code quite a bit. Even if you don't agree, there are still some major changes that need to be taken into account if you are using 0.7.0+. Below is the include section that must be used if you are using CEGUI 0.7.1:

#include "RendererModules/Ogre/CEGUIOgreRenderer.h"

// CEGUI includes
#include "CEGUISystem.h"
#include "CEGUIInputEvent.h"
#include "CEGUIWindow.h"
#include "CEGUIWindowManager.h"
#include "CEGUISchemeManager.h"
#include "CEGUIFontManager.h"
#include "elements/CEGUIFrameWindow.h"


In this case, the first include has been changed because the OgreRenderer is no longer an automatic part of the Ogre release but has been left in the CEGUI code with a name change to match the pattern of the other renderers included in that package. Now, on with the show. I will highlight changes that must be made to use 0.7.1 as this tutorial continues along. I happen to have my paths set up for CEGUI much like I have for Ogre. That is, the include directory for CEGUI is part of the other includes path. You may have set things up differently. In that case, you need to alter the top #include to suit your setup.

We will be creating an instance of the GUI renderer, so we need to reference its header. You'll find that header in the SDK or Ogre source under Samples/include (which was added to the project's Additional Include Directories paths). The CEGUI headers can be found in the SDK under include/CEGUI (which we also added to the include paths). For those used to seeing "CEGUI/CEGUISystem.h", that way was finally abandoned in CEGUI — all of the CEGUI files now have that directory prefix removed. If you are having trouble compiling as a result, feel free to add both "include/CEGUI" and "include" (where "include" contains the "CEGUI" subdirectory). I probably did not need to include the "elements/CEGUIFrameWindow.h" file above; it's probably lingering from an old version of source that was cut-and-pasted. ;) It's harmless.

Initializing CEGUI

main.cpp

// with a scene manager and window, we can create a the GUI renderer
	CEGUI::OgreCEGUIRenderer* pGUIRenderer = new CEGUI::OgreCEGUIRenderer(
		window,					// the render window created earlier; CEGUI renders to this
		Ogre::RENDER_QUEUE_OVERLAY,		// CEGUI should render in this render queue
		false,					// put everything in the above render queue first, not last
		3000,					// this is actually unnecessary now in CEGUI -- max quads for the UI
		sceneMgr				// use this scene manager to manage the UI
	);

	// create the root CEGUI class
	CEGUI::System* pSystem = new CEGUI::System(pGUIRenderer);

	// tell us a lot about what is going on (see CEGUI.log in the working directory)
	CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);

	// use this CEGUI scheme definition (see CEGUI docs for more)
	CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme", (CEGUI::utf8*)"GUI");

	// show the CEGUI mouse cursor (defined in the look-n-feel)
	pSystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");

	// use this font for text in the UI
	CEGUI::FontManager::getSingleton().createFont("Tahoma-8.font", (CEGUI::utf8*)"GUI");
	pSystem->setDefaultFont((CEGUI::utf8*)"Tahoma-8");

	// load a layout from the XML layout file (you'll find this in resources/gui.zip), and 
	// put it in the GUI resource group
	CEGUI::Window* pLayout = CEGUI::WindowManager::getSingleton().loadWindowLayout("katana.layout", "", "GUI");

	// you need to tell CEGUI which layout to display. You can call this at any time to change the layout to
	// another loaded layout (i.e. moving from screen to screen or to load your HUD layout). Note that this takes
	// a CEGUI::Window instance -- you can use anything (any widget) that serves as a root window.
	pSystem->setGUISheet(pLayout);


Here, too, there are several changes if you are using CEGUI 0.7.0+. The way that CEGUI is initialized has changed and is much simpler to read. Below is the CEGUI 0.7.1 code for this section.

// with a scene manager and window, we can create a the GUI renderer

	// new way to instantiate a CEGUIOgreRenderer (Ogre 1.7)
	Ogre::RenderTarget *mRenderTarget = window;
	CEGUI::OgreRenderer* pGUIRenderer = &CEGUI::OgreRenderer::bootstrapSystem(*mRenderTarget);

	// create the root CEGUI class
	CEGUI::System* pSystem = CEGUI::System::getSingletonPtr();

	// tell us a lot about what is going on (see CEGUI.log in the working directory)
	CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);

	// use this CEGUI scheme definition (see CEGUI docs for more)
	CEGUI::SchemeManager::getSingleton().create((CEGUI::utf8*)"TaharezLookSkin.scheme", (CEGUI::utf8*)"GUI");

	// show the CEGUI mouse cursor (defined in the look-n-feel)
	pSystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");

	// use this font for text in the UI
	CEGUI::FontManager::getSingleton().create("Tahoma-8.font", (CEGUI::utf8*)"GUI");
	pSystem->setDefaultFont((CEGUI::utf8*)"Tahoma-8");

	// load a layout from the XML layout file (you'll find this in resources/gui.zip), and 
	// put it in the GUI resource group
	CEGUI::Window* pLayout = CEGUI::WindowManager::getSingleton().loadWindowLayout("katana.layout", "", "GUI");

	// you need to tell CEGUI which layout to display. You can call this at any time to change the layout to
	// another loaded layout (i.e. moving from screen to screen or to load your HUD layout). Note that this takes
	// a CEGUI::Window instance -- you can use anything (any widget) that serves as a root window.
	pSystem->setGUISheet(pLayout);


The important changes here are the new way to instantiate the CEGUI OgreRenderer. Note the class name has changed from CEGUI::OgreCEGUIRenderer to CEGUI::OgreRenderer. In addition the large initialization function call has been reduced to a nice neat single (or no) parameter function, thus handling a number of initialization features on its own based on the renderer you pass to it. At any rate, the initialization section is below:

Ogre::RenderTarget *mRenderTarget = window;
	CEGUI::OgreRenderer* pGUIRenderer = &CEGUI::OgreRenderer::bootstrapSystem(*mRenderTarget);

	// create the root CEGUI class
	CEGUI::System* pSystem = CEGUI::System::getSingletonPtr();


We need a pointer to the Ogre RendererTarget rather than an Ogre window. It is through this that the bootstrapSystem function will extrapolate all of the information that you used to have to pass in the old instantiation method.

We also still want a pointer to the CEGUI System and that is what the final line above does for us.

This code is liberally commented so I won't repeat much here. Note that we use the Ogre render window that was created in main.cpp ("window") as well as the scene manager ("sceneMgr") so you will find this code in main.cpp following the creation and setup of those objects.

Input Support For CEGUI


We needed to change the signature of the InputHandler class to take an additional parameter: a pointer to the instance of CEGUI::System we created in main.cpp. This is because now the input handler is responsible for pushing input events into CEGUI:

input.cpp

// MouseListener
bool InputHandler::mouseMoved(const OIS::MouseEvent &evt) {
	m_pSystem->injectMouseWheelChange(evt.state.Z.rel);
	return m_pSystem->injectMouseMove(evt.state.X.rel, evt.state.Y.rel);
}

bool InputHandler::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) {
	CEGUI::MouseButton button = CEGUI::NoButton;

	if (btn == OIS::MB_Left)
		button = CEGUI::LeftButton;
	
	if (btn == OIS::MB_Middle)
		button = CEGUI::MiddleButton;
	
	if (btn == OIS::MB_Right)
		button = CEGUI::RightButton;

	return m_pSystem->injectMouseButtonDown(button);
}

bool InputHandler::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) {
	CEGUI::MouseButton button = CEGUI::NoButton;

	if (btn == OIS::MB_Left)
		button = CEGUI::LeftButton;
	
	if (btn == OIS::MB_Middle)
		button = CEGUI::MiddleButton;
	
	if (btn == OIS::MB_Right)
		button = CEGUI::RightButton;
	
	return m_pSystem->injectMouseButtonUp(button);
}

		
// KeyListener
bool InputHandler::keyPressed(const OIS::KeyEvent &evt) {
	unsigned int ch = evt.text;

	m_pSystem->injectKeyDown(evt.key);
	return m_pSystem->injectChar(ch);
}

bool InputHandler::keyReleased(const OIS::KeyEvent &evt) {
	if (evt.key == OIS::KC_ESCAPE)
		m_simulation->requestStateChange(SHUTDOWN);

	return m_pSystem->injectKeyUp(evt.key);
}


Note: returning the results of injectChar and injectKeyUp can result in keystrokes (to say, an edit box) being lost when typing quickly. You may want to always return true instead, so that all buffered keystrokes will be processed.

It should be fairly evident how input events make it into CEGUI from this code — the "inject***" calls in the code above (which are methods on CEGUI::System). You can make these calls from anywhere that you have access to the input events; I do it in the input handler class because it's convenient and the rest of the application really doesn't need to worry about it or even know about it — it will get notified of UI actions through a different data path.

main.cpp

// since the input handler deals with pushing input to CEGUI, we need to give it a pointer
	// to the CEGUI System instance to use
	InputHandler *handler = new InputHandler(pSystem, sim, hWnd);

	// put us into our "main menu" state
	sim->requestStateChange(GUI);


We also now change to the "GUI" state instead of "SIMULATION" as we did in the previous article — in a normal app you typically would go first into a "main menu" state instead of going directly into the game, so this state change follows that pattern. You would also deal with creating a class to deal with UI actions in your state manager; fro simplicity (and since we are not changing states at this time) we just do it in main.cpp:

// make an instance of our GUI sheet handler class
	MainMenuDlg* pDlg = new MainMenuDlg(pSystem, pLayout, sim);


This action handler class is new to this article; you'll find its declaration and definition in MainMenuDlg.h and .cpp:

MainMenuDlg.h

#pragma once

#include "CEGUIWindow.h"

namespace CEGUI
{
	class System;
	class Window;
}

class Simulation;

class MainMenuDlg
{
public:
	MainMenuDlg(CEGUI::System* pSystem, CEGUI::Window* pSheet, Simulation* pSimulation);
	~MainMenuDlg();

	// CEGUI event handlers. You can name these whatever you like, so long as they have the proper 
	// signature: bool <method name>(const CEGUI::EventArgs &args)
	bool Quit_OnClick(const CEGUI::EventArgs &args);
	bool Options_OnClick(const CEGUI::EventArgs &args);
	bool Launch_OnClick(const CEGUI::EventArgs &args);

private:
	CEGUI::System* m_pSystem;	// pointer to the CEGUI System instance
	CEGUI::Window* m_pWindow;	// pointer to the layout sheet window
	Simulation* m_pSimulation;	// pointer to the Simulation controller 
};


MainMenuDlg.cpp

#include "MainMenuDlg.h"
#include "Simulation.h"
#include "CEGUISystem.h"
#include "CEGUIWindow.h"
#include "CEGUIWindowManager.h"
#include "elements/CEGUIPushButton.h"

MainMenuDlg::MainMenuDlg(CEGUI::System *pSystem, CEGUI::Window *pSheet, Simulation *pSimulation)
{
	m_pSystem = pSystem;
	m_pWindow = pSheet;
	m_pSimulation = pSimulation;

	// hook up the event handlers to the window elements
	CEGUI::PushButton* pQuitButton = (CEGUI::PushButton *)CEGUI::WindowManager::getSingleton().getWindow("cmdQuit");
	pQuitButton->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&MainMenuDlg::Quit_OnClick, this));

	CEGUI::PushButton* pOptionsButton = (CEGUI::PushButton *)CEGUI::WindowManager::getSingleton().getWindow("cmdOptions");
	pOptionsButton->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&MainMenuDlg::Options_OnClick, this));

	CEGUI::PushButton* pLaunchButton = (CEGUI::PushButton *)CEGUI::WindowManager::getSingleton().getWindow("cmdInstantAction");
	pLaunchButton->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&MainMenuDlg::Launch_OnClick, this));
}

MainMenuDlg::~MainMenuDlg()
{
}

bool MainMenuDlg::Quit_OnClick(const CEGUI::EventArgs &args)
{
	m_pSimulation->requestStateChange(SHUTDOWN);
	return true;
}

bool MainMenuDlg::Launch_OnClick(const CEGUI::EventArgs &args)
{
	return true;
}

bool MainMenuDlg::Options_OnClick(const CEGUI::EventArgs &args)
{
	return true;
}


The two primary things to observe are (a) how the action handler methods are hooked up to CEGUI events, and (b) that we handle the "Quit" button click by telling the application to shut down (same as we did in the ESC keypress handler). If you are feeling frisky, you can make your own layouts and add code to the other stubbed handlers (Launch_OnClick and Options_OnClick) to push different GUI sheets onto the display (using the "loadWindowLayout" call as above in main.cpp).

Conclusion


There really is not that much more to it than that — how you set up your UI action handlers, and how you feed input into CEGUI, is entirely up to you. You can use the code here, or devise your own, but the basics don't change. And there really is not much more to say on getting started with CEGUI and Ogre — the rest revolves around making layouts (and maybe even different schemes and looks-and-feels), as well as creating the hooks from CEGUI element actions into your code.

Download The Code


As mentioned, I migrated the code and projects to Ogre 1.4.x (Eihort) and also the VC8 project file formats. You can download the code and it should build right out of the box if you have the Ogre 1.4.x SDK installed (and are using VC8, of course). Note that the project copies the executable to the $(OGRE_HOME)/bin/Debug folder — you may need to set your Working Directory in Visual Studio to reflect that, and you certainly will need to copy the gui.zip file from the Debug folder in the PracticalApp source tree to a "resource" folder in $(OGRE_HOME)/bin/Debug (you may need to create this folder). You may also be able to run it from the Debug folder in the PracticalApp source tree if your PATH contains $(OGRE_HOME)/bin/Debug.

Note as well that I only set up the projects in the Debug configurations — it is expected that you would use this code in a different project/solution of your own, so if you instead use the PracticalApp solution as your starting point and intend to build Release configs, you probably will need to set up the project settings properly for that config.

Enjoy!

CEGUI 0.7.1 note


The above linked code in the Download The Code section is not for 0.7.1. I do not have a full source and gui.zip package compiled together for you. The changes outlined earlier in this tutorial should suffice to get you going. There are some other changes with CEGUI 0.7.0+ that you should be aware of. Without getting out of scope for this tutorial, suffice it to say that the old tag <WindowSet> is now <WindowRendererSet> and the CEGUIFalagardBase is now CEGUIFalagardWRBase. The .scheme files had to be changed to reflect this or you will get many crashes and not necessarily easily see why. Here is the new gui.zip file you can use with this tutorial and CEGUI 0.7.1.

CEGUI 0.8.3 note

There are some not easy to find changes for CEGUI 8.3. The source code and a Makefile for a Linux distro can be downloaded from
 Plugin disabled
Plugin attach cannot be executed.
. I removed any unnecessary file from gui.zip, since most xml files would not work with this version of CEGUI.


Prev <<< Practical Application - Let's Get StartedPractical Application - I Haven't Decided What's Next >>> Next


Alias: Practical_Application_-_Something_With_A_Bit_More_Meat