Prerequisites
This tutorial assumes that you already have C ++ programming basics and can successfully establish and compile the Ogre program. (If you still have problems setting the program, see settingupanapplication for details ). This tutorial is based on the previous tutorials for beginners and assumes that you have learned them.
[Edit]
Introduction
In this lesson, we will explore how to use cegui (an embedded GUI system) in ogre ). After completing this course, you should be able to add basic cegui functions to your ogre application. Note: This course does not fully teach you how to use cegui. This course is just an entry. For more in-depth questions, go to their home page.
You can find the code for this lesson here. In this lesson, you should add code to your project one by one, compile it, and observe its effect.
[Edit]
From here
[Edit] initial code
In this lesson, you will use the predefined basic code as the starting point. As long as you have learned the previous courses, you should be familiar with them. Use your favorite compiler to create a project, and then create a new source file containing the following code:
#include "ExampleApplication.h"#include #include #include class TutorialListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener{public: TutorialListener(RenderWindow* win, Camera* cam) : ExampleFrameListener(win, cam, true, true) { mContinue=true; mMouse->setEventCallback(this); mKeyboard->setEventCallback(this); } // CEGUIDemoListener bool frameStarted(const FrameEvent &evt) { mKeyboard->capture(); mMouse->capture(); return mContinue && !mKeyboard->isKeyDown(OIS::KC_ESCAPE); } bool quit(const CEGUI::EventArgs &e) { mContinue = false; return true; } // MouseListener bool mouseMoved(const OIS::MouseEvent &arg) { return true; } bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id) { return true; } bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id) { return true; } // KeyListener bool keyPressed(const OIS::KeyEvent &arg) { return true; } bool keyReleased(const OIS::KeyEvent &arg) { return true; }private: bool mContinue;};class CEGUIDemoApplication : public ExampleApplication{public: CEGUIDemoApplication() : mSystem(0), mRenderer(0) { } ~CEGUIDemoApplication() { if (mSystem) delete mSystem; if (mRenderer) delete mRenderer; }protected: CEGUI::System *mSystem; CEGUI::OgreCEGUIRenderer *mRenderer; void createScene(void) { } void createFrameListener(void) { mFrameListener= new TutorialListener(mWindow, mCamera); mFrameListener->showDebugOverlay(true); mRoot->addFrameListener(mFrameListener); }};#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32#define WIN32_LEAN_AND_MEAN#include "windows.h"INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)#elseint main(int argc, char **argv)#endif{ // Create application object CEGUIDemoApplication app; try { app.go(); } catch(Exception& e) {#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 MessageBoxA(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);#else fprintf(stderr, "An exception has occurred: %s/n", e.getFullDescription().c_str());#endif } return 0;}
Make sure that you can compile and run the code. Currently, this program cannot display anything except a black screen (exit by pressing ESC. If you encounter a connector error during compilation, make sure that ceguibase_d.lib and ogreguirenderer_d.lib have been added to the connector input (in debug mode, if it is in release mode, remove the suffix _ D ).
[Edit] Brief Introduction
Cegui is a fully functional GUI library that can be embedded into 3D applications such as ogre (of course only DirectX and OpenGL are supported ). Just like ogre is just an image library (without sound, physical, and other things), cegui is just a GUI library, meaning that it does not render itself, it is not linked to any mouse or Keyboard Events. In fact, in order for the cegui to be rendered, you must provide it with a Renderer (included in the ogreguirenderer library of the SDK ). To make it understand mouse and keyboard events, you must manually inject them into the system. This may seem painful at first, but it only requires a little bit of code. In this way, you can fully control rendering and input, and cegui will not block you at all.
Cegui has many aspects and many strange things you are not familiar with (even if you have used a GUI system before ). In the next study, I will give you one by one.
[Edit] integration with ogre
[Edit] initialize cegui
In the previous tutorial, we have learned how to start cegui, so we will not go into detail in the first part. Find the createscene function and add the following code:
mRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr); mSystem = new CEGUI::System(mRenderer);
Okay, the cegui has been initialized, and we choose the skin it will use. Cegui is highly customized and allows you to define the "look and feel" of an application by replacing the skin ". We will not discuss how to skin this database, so if you want to know more, consult the cegui website. The following code selects a skin:
CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");
The default ogre installation does not provide you with any other skins, but if you download it from the cegui website, you can find several other options (or you can make it yourself ). The next task is to set the default mouse pointer image and default font:
mSystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow"); mSystem->setDefaultFont((CEGUI::utf8*)"BlueHighway-12");
In the entire tutorial, we will use cegui to display the mouse pointer, even if we do not use other features of this GUI library. You can also use other GUI Libraries to render the mouse, or use ogre to create your own mouse (the latter requires a little trouble ). If you only use cegui for mouse pointer and worry about memory usage and hard disk space occupied by the game, you can find one of these options to replace cegui.
Note that the default mouse pointer is set in the last code, but we didn't directly use the mousecursor: setimage function as in the subsequent tutorial. This is because in this lesson, we are always on the cegui window (although it may be invisible), and setting the default mouse pointer will make it the image we choose. If we directly set the mouse pointer without setting the default one, the mouse pointer is invisible whenever it passes through the cegui window (this is always the case in this lesson ). On the other hand, if you do not display any cegui window, setting the default mouse indicator will not produce any effect. This is the case in the subsequent tutorial. In this case, you can call mousecursor: setimage to display the pointer in the application.
[Edit] Inject Keyboard Events
Cegui does not process input by itself, but does not read the mouse movement and keyboard input. On the contrary, it relies on the user to inject the mouse button event to the system. What we need to do next is to handle these events. If you are using cegui, you need to run the mouse and keyboard in buffer mode, so that you can directly obtain events and inject them when they occur. Find the keypressed function and add the Code:
CEGUI::System *sys = CEGUI::System::getSingletonPtr(); sys->injectKeyDown(arg.key); sys->injectChar(arg.text);
After getting the system object, Let's do two things. The first thing is to inject the key down event into the cegui. The second is to inject a character that is actually pressed. Correct injection of characters is very important, because if a non-English keyboard is used, injecting a keydown event alone cannot always get the desired result. Remember that injectchar supports Unicode.
Now we inject a keyup event to the system. Find the keyreleased function and add the following code:
CEGUI::System::getSingleton().injectKeyUp(arg.key);
Note: we do not need to inject the character up event here, so you can use keyup.
[Edit] Change and inject mouse events
Now that we have completed the keyboard input processing, let's take a look at the mouse input. However, we have a small question to address. When we inject the kepup and kepdown events into the cegui, we do not need to convert the key code. OIS and cegui use the same keycode for keyboard input. But the mouse button is not like this. When you press the mouse button to inject to cegui, you need to write a function to convert the OIS button ID to the cegui button ID. Near the top of your code, add the following function before the tutoriallistener class:
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; }}
Now we are ready to inject mouse events. Find the mousepressed function and add the following code:
CEGUI::System::getSingleton().injectMouseButtonDown(convertButton(id));
The meaning of this code should be clear. We convert the input ID and then pass the result to cegui. Find the mousereleased function and add the following line:
CEGUI::System::getSingleton().injectMouseButtonUp(convertButton(id));
Finally, we inject the mouse into the cegui. The cegui: The system object has an injectmousemove method, which requires the relative operation of the mouse as a parameter. Ois: The mousemoved handler provides these relative motion in the state. X. Rel and the state. Y. rel variables. Find the mousemoved method and add the following code:
CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
Okay! After the cegui settings are complete, you can receive mouse and keyboard events.
[Edit] windows, forms, and components
[Edit] Introduction
Cegui is different from most other GUI systems. In cegui, everything that can be displayed is a subclass of the cegui: window class, and a window can have any number of subwindows. This will lead to some strange things. You can place another button in a button, though this is not actually the case. The reason I mentioned this is that when you are looking for a special widget in an application, they are all called windows and can be accessed by accessing Windows functions.
The most common usage of cegui is that you do not have to create every single object in the code. You can use an editor like cegui layout editor to create a GUI layout for your program. Based on your preferences, after placing your windows, buttons, and other components on the screen, the editor will save the layout to a text file. You can then load the layout to the GUI sheet (it is also a subclass of cegui: Window ).
Finally, you need to know that the cegui contains a large number of widgets for your program. We will not discuss it here, so if you decide to use cegui, you 'd better go to their website to learn more.
[Edit] Load Form
Loading a form (sheet) in cegui is very easy. The windowmanager class provides a "loadwindowlayout" function to load a form and put it into the cegui: Window object. Then you can use cegui: System: setguisheet to display it. We won't use it in this lesson, but if I don't introduce you to the example of using it, I think I am very remiss. Do not add these to the Code (if they are added, delete them after seeing the results ):
// Do not add this to the program CEGUI::Window* sheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"ogregui.layout"); mSystem->setGUISheet(sheet);
In this way, the form is set to display. You can retrieve the form later, using the system: getguisheet method. By calling setguisheet, You can seamlessly exchange GUI forms (but you must ensure that you have a pointer to the current form if you want to change it back ).
[Edit] manually create a part
As I mentioned earlier, most of the time you use cegui, you use a GUI form generated by the editor. However, sometimes you need to manually create a part to render it on the screen. In this example, we will add a Quit button and we will add features to it later. Because at the end of this lesson, we will not only add a Quit button to the screen, we will create a default cegui: window to contain all the parts we want to create. Add the following code to the end of the createscene function:
CEGUI::WindowManager *win = CEGUI::WindowManager::getSingletonPtr(); CEGUI::Window *sheet = win->createWindow("DefaultGUISheet", "CEGUIDemo/Sheet");
Here, windowmanager is used to create a "defaultguisheet" called "ceguidemo/sheet ". Although we may take any name for the form, it is very common (recommended) to name it using the hierarchical structure method, such as "someapp/mainmenu/submenu3/cancelbutton ". Then, we need to create a Quit button and set its size:
CEGUI::Window *quit = win->createWindow("TaharezLook/Button", "CEGUIDemo/QuitButton"); quit->setText("Quit"); quit->setSize(CEGUI::UVector2(CEGUI::UDim(0.15, 0), CEGUI::UDim(0.05, 0)));
The meaning of this is relatively obscure. The size and orientation of cegui use a uniform scale ". When setting the size, you must create a udim object to tell it the size. The first parameter is the relative size of the object compared to its parent. The second parameter is the absolute size (in pixels) of the object ). Note that you can only set one of the two parameters for udim, And the other must be 0. So here we create a button with a 15% x 5% width and a x height. If we want to set it to 20*5 pixels, we need to set the second parameters of two udim to 20 and 5 respectively.
The last thing we need to do is to bond the Quit button to the form we created and set it to the GUI form of the current system:
sheet->addChildWindow(quit); mSystem->setGUISheet(sheet);
Now, if you compile and run your program, you will see a Quit button in the upper-left corner of the screen, but there will be no response when you click it.
[Edit] Events
Events in cegui are very flexible. Instead of implementing an interface for receiving events, it uses a callback mechanism to bind any public function to an event processor (signature through appropriate methods ). Unfortunately, this also means that registration events are more troublesome than ogre. Now we want to register the click event for processing the Quit button. In this case, we need a pointer to the Quit button first. Locate the tutoriallistener constructor and add the following code:
CEGUI::WindowManager *wmgr = CEGUI::WindowManager::getSingletonPtr(); CEGUI::Window *quit = wmgr->getWindow((CEGUI::utf8*)"CEGUIDemo/QuitButton");
Now we have obtained the pointer to the button. We can subscribe to this click event. All parts in the cegui have the event sets they support, and they all start with "Event. Here is the code for subscribing to events:
quit->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&TutorialListener::quit, this));
The first parameter of subscribeevent is the event itself. The second parameter is an event: subscriber object. When creating a subcriber object, the first thing we pass in is a pointer to the event processing function (Note & Symbol provides the function pointer ). The second parameter passed through subscriber is the tutoriallistener object (that is, the "This" object) that processes this event ). Okay! Our tutoriallistener: Quit function (defined) processes mouse clicks and terminates the program.
Compile and run your program and debug it.
Note: You can create any number of functions to process events for the cegui. The only constraint on them is that the return value must be a boolean type and must have a unique parameter of the type "const cegui: eventargs. For more information about the event (and how to unsubscribe to the event), refer to the cegui website.
[Edit] rendering to texture
We can use cegui to do more interesting things. One of them is to create a texture window Renderer. This allows us to create a view that can be directly rendered to the cegui component. In this case, we must first create a scenario that we can see. Add the following code at the bottom of the createscene function:
mSceneMgr->setAmbientLight(ColourValue(1, 1, 1)); mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8); Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh"); SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(0, 0, -300)); headNode->attachObject(ogreHead);
Now we have created a rendering texture. The rendersystem object provides a texture rendering function. We use the rendersystem: createrendertexture function to create a texture. In this program, we create a 512x512 texture:
RenderTexture *tex = mRoot->getRenderSystem()->createRenderTexture("RttTex", 512, 512, TEX_TYPE_2D, PF_R8G8B8);
UPDATE: The createrendertexture () method appears to have been deprecated in the trunk. So instead of the above line use this:
RenderTexture *tex= mRoot->getTextureManager()->createManual ("RttTex", "Default", TEX_TYPE_2D, 512, 512, 0, PF_R8G8B8, TU_RENDERTARGET) ->getBuffer()->getRenderTarget();
For more information about this function, see api reference. Next we need to create a camera and a viewport to view the scenario we created. Note that we have changed some of the viewport settings, including disabling overlays. This is very important, otherwise you will see that the cegui and ogre overlap in the small window.
Camera *cam = mSceneMgr->createCamera("RttCam"); cam->setPosition(100, -100, -400); cam->lookAt(0, 0, -300);
Viewport *v = tex->addViewport(cam); v->setOverlaysEnabled(false); v->setClearEveryFrame(true); v->setBackgroundColour(ColourValue::Black);
Note that we add the view to the texture (and we usually add the view to the rendering window ). Now that we have created our scenarios and textures, We need to implant them into cegui. You can use any Ogre texture to create the cegui: texture. by calling the ogreceguirenderer: createtexture function:
CEGUI::Texture *cTex = mRenderer->createTexture((CEGUI::utf8*)"RttTex");
Unfortunately, this is complicated. In cegui, you never process a single texture or image. Cegui processes image sets rather than individual images. When you try to define the look and feel of the skin, it is very useful to process a set of images (for example, look at the taharezlook. TGA image in the media directory in the SDK ). Then, even if you only need to define a single image, you must create an image set for it. Next we will do this:
CEGUI::Imageset *imageSet = CEGUI::ImagesetManager::getSingleton().createImageset((CEGUI::utf8*)"RttImageset", cTex); imageSet->defineImage((CEGUI::utf8*)"RttImage", CEGUI::Point(0.0f, 0.0f), CEGUI::Size(cTex->getWidth(), cTex->getHeight()), CEGUI::Point(0.0f,0.0f));
The first line uses the texture we provide to it to create an image set (called "rttimageset "). The second line (call defineimage) specifies the first and only image, which is called "rttimage" and is the same size as the ctex texture we provide. Finally, we need to create a staticimage widget to accommodate this rendered texture. The first part is no different from creating other windows:
CEGUI::Window *si = win->createWindow((CEGUI::utf8*)"TaharezLook/StaticImage", "RTTWindow"); si->setSize(CEGUI::UVector2(CEGUI::UDim(0.5f, 0), CEGUI::UDim(0.4f, 0))); si->setPosition(CEGUI::UVector2(CEGUI::UDim(0.5f, 0), CEGUI::UDim(0, 0)));
Now we need to specify the image to be displayed for the staticimage part. Once again, since cegui always processes image sets rather than individual images, we must get the image name from the image set and display it:
si->setProperty("Image", CEGUI::PropertyHelper::imageToString(&imageSet->getImage((CEGUI::utf8*)"RttImage")));
It seems that we have packaged a texture into an image set and then unwrapped it. This is what we actually do. Operating images in cegui is not the simplest and most direct event in this library. Finally, we add the staticimage component to the previously created GUI form:
sheet->addChildWindow(si);
Now we have finished. Compile and run the application.
[Edit] Conclusion
[Edit] other options
Cegui is not perfect. It is definitely not applicable to everyone. In addition to cegui, there are other options:
Navi Right Brain Games GUI quickgui betagui
[Edit] More Information
You can also find more information about cegui in other places.
Practical Application-something with a bit more meat-a more in-depth tutorial than the one here. cegui's official tutorials the cegui website