Ogre基础教程7:CEGUI 与 Ogre(未完成)

教程介绍

本篇教程中,我们将探索如何在Ogre中使用CEGUI。本篇教程研习结束时,你将可以添加基本的CEGUI功能到你的应用中。注意:本教程不打算讲解CEGUI完整的使用技术,只是引领入门。所有关于CEGUI的深入问题和帮助请移步它们的主页。

1 预备知识

2 库版本

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

注意!CEGUI 0.8.x 接口已被改变。本教程的大部分仍是可用的,但一些语法有所不同。我(作者)已已在不同于0.7版本的地方添加了代码。

目录

1 预备知识
2 库版本
3 开始
    3.1 初始化代码
    3.2 项目设置
        3.2.1 windows设置
            3.2.1.1 项目包含目录
            3.2.1.2 项目库目录
            3.2.1.3 项目附加依赖库
            3.2.1.4 移动动态链接库
        3.2.2 Linux设置
    3.3 编译代码
4 对CEGUI的一个简单介绍
5 与Ogre整合
    5.1 定义 CEGUI 资源组
    5.2 初始化CEGUI
    5.3 移除SDKTrays
        5.3.1 createFrameListener
        5.3.2 frameRenderingQueued
    5.4 注入键盘事件
    5.5 转化并注入鼠标事件
6 窗口,表单,以及窗体
    6.1 介绍
    6.2 导入一个表单
    6.3 手动创建一个对象
7 事件
8 渲染到纹理
9 总结

3 开始

3.1 The Initial Code

将你BasicTutorial7类的头文件如下编辑:

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);
};

BasicTutorial7的实现文件如下:

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>

3.2 项目设置

3.2.1 Windows 设置

你将需要CEGUI,以及Ogre的CEGUI渲染。查看Building CEGUI的介绍。
然后,你需要将CEGUI的头文件位置,添加到你项目的包含目录中。这里的代码假设你已经准备好了一个CEGUI的SDK,其目录布置如下:
这里写图片描述
在编译了CEGUI后,你可以复制文件来产生一个SDK。这样做完全是可选的,尽管这里的介绍基于以上的布置。

3.2.1.1 项目包含目录

将以下内容添加到你项目的包含目录列表中(注意,这些将根据你CEGUI版本而改变):

CEGUI_HOME/include
3.2.1.2 项目库目录
CEGUI_HOME/lib
3.2.1.3 项目附加依赖库
Debug
CEGUIBase_d.lib
CEGUIOgreRenderer_d.lib
Release
CEGUIBase.lib
CEGUIOgreRenderer.lib

CEGUI 0.8.x

#Release
CEGUIBase-0.lib
CEGUIOgreRenderer-0.lib
3.2.1.4 移动DLL

在Windows系统中,你需要将以下的DLL文件复制到你的OGRE_HOME/bin/debug 目录中:

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

最后一个DLL假设你正在使用默认的ExpatXML解析器。

CEGUI 0.8.x

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

最后一个Dll可以在CEGUI依赖的build中找到。

3.2.2 Linux 设置

为了在Linux下构建本教程,你需要构建并安装一个CEGUI的最新版本,然后修改你的构建来包含CEGUI和CEGUI-OGRE库。这些如何进行,依赖于你正在使用的构建类型。如果你正在使用来自 的自动工具 Setting Up An Application - Autotools - Linux,那么你需要添加以下内容到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)

并且将 Makefile.ac 做如下改动:

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

如果你使用CMake构建,那么你需要编辑CMakeLists.txt来添加那些库。

3.3 Compile the Code

在继续之前,请确定你可以编译并运行代码。除了给你展示一个空白的屏幕(按下Esc退出),程序应该没有做任何其它的事。如果你遭遇了编译或链接错误,请检查你的设置。

4 对CEGUI的一个简单介绍

CEGUI是一个可以被嵌入到如Ogre这样的3D应用中的,完全特定的GUI库。它也支持OpenGL,DirectX,以及鬼火Irrlicht。类似Ogre仅是一个图形库(并且不完成如声音、物理这样的其它工作),CEGUI知识一个GUI库,意味着它不完成渲染自身,或将自己勾连到任何鼠标、键盘事件上。实际上,为了渲染CEGUI,你必须为它提供一个渲染器(随CEGUI的CEGUIOgreRenderer库);为了让它获悉鼠标和键盘事件,你必须手动地将它们插入到系统。这在开始阶段看起来令人痛苦;但实际上仅需少量代码即可完成。它也允许你对输入和渲染有完整的控制;CEGUI永远不会越俎代庖。
CEGUI有许多方面,并且对你而言将会出现许多怪异的内容(即使你在之前已经使用过GUI系统)。我将尝试慢慢将它们讲解。

5 与Ogre整合

5.1 定义CEGUI资源组

CEGUI像Ogre那样,需要多种类型的资源以运行。它有几种资源管理器(像Ogre那样),需要找到它们对应的资源位置;所以,你需要在resources.cfg中定义必要的资源组以及它们的位置。
CEGUI的资源可以在不同的位置找到,取决于你的平台。对于Linux,它们通常全部安装到/usr/local/share/CEGUI or /usr/share/CEGUI 。

添加以下到resources.cfg;将“path_to_cegui”替换为CEGUI数据文件的路径:

[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

注意,如前文提到的,例如在Ubuntu10.04LTS上:为了载入GUI方案,路径描述 “FileSystem=/usr/local/share/CEGUI/xml_schemas” 必须被添加到resources.cfg文件的[General]区域。

5.2 初始化CEGUI

找到createScene函数,并且添加以下代码:
mRenderer = &CEGUI::OgreRenderer::bootstrapSystem();
现在,CEGUI已经被初始化,我们需要为每个CEGUI的资源管理器,设置所谓的默认资源组。添加以下代码:

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

CEGUI 0.8.x 第一行(Imageset::…) 改为
CEGUI::ImageManager::setImagesetDefaultResourceGroup("Imagesets");

如你所见,我们使用了在resources.cfg中定义的资源组。
CEGUI是高度可定制的,并且允许你定义应用的外观和感觉,通过改变它的皮肤(按CEGUI的说法是scheme)。在任何教程中,我们将不涉及如何改变皮肤;所以如果你希望学习这些,查阅CEGUI的网页。
接下来这行代码选择了皮肤:
CEGUI::SchemeManager::getSingleton().create("TaharezLook.scheme");
我们下一步需要做的是设置默认鼠标光标:
CEGUI::System::getSingleton().setDefaultMouseCursor("TaharezLook", "MouseArrow");
第一个参数定制了图片集合(Imageset),第二个参数定制在图片集合中,要使用的图片名称。
CEGUI 0.8.x 设置鼠标图片的语法在0.8.x中有所不同:

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

自始至终,本教程系列会使用CEGUI来显示鼠标光标,就算是我们不使用GUI库的其它功能。你可以使用另一个GUI库来渲染鼠标,或者直接地使用Ogre仅仅创建你自己的鼠标光标(后者会有一点复杂)。如果你仅为了鼠标光标而使用CEGUI,并且关心内存使用或你游戏占用的磁盘空间,你可以用其它选择代替CEGUI。
最后请注意,最后的代码片段我们已经设置了默认的鼠标光标,但我们没有直接地使用MouseCursr::setImage函数,如我们将在后续教程中做的那样。这是因为在本教程中,我们会总是在某些CEGUI窗口下(尽管它可能是不可见的),所以设置默认的光标,实际上将会使鼠标光标变成我们选择的图片。
如果我们直接地设置鼠标光标,并且不设置默认,鼠标光标将在每次它穿过一个CEGUI窗口时不可见(本篇教程中,是始终不可见)。另一方面:如果你没有任何CEGUI窗口显示,设置默认鼠标图像什么也不做,这和后续教程一样。在那种情形中,调用MouseCursor::setImage()将为应用显示光标。例如:

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

5.3 移除 SDKTrays

在继续之前,我们需要从我们的应用中移除OgreBits SDKTrays 。我们通过重写两个函数:createFrameListener()frameRenderingQueued()来完成。

5.3.1 createFrameListener

BaseApplication::createFrameListener函数的内容复制到BasicTutorial7::createFrameListener中,并且删除SDKTrays被创建的部分以及debug overlay设置。
当我们去除它后看起来会是这样:

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);
}

5.3.2 frameRenderingQueued

我们也需要改写BaseApplication::frameRenderingQueued
让它看起来是这样:

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;
}

我们移除了mTrayMgr和mDetailsPanel部分,并且让CEGUI系统知道时间的流逝。

5.4 注入键盘事件

CEGUI不以任何方式掌控输入。它不读取鼠标移动或键盘输入,而是依赖于用户将键盘和鼠标事件注入到系统中。我们要做的下一件事,是处理键盘输入。如果你与CEGUI协作,你将需要拥有缓冲模式的鼠标和键盘,这样你可以直接接收事件,并且当它们发生时注入它们。
找到keyPressed函数,并且添加以下代码(替换掉原有的内容):

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

CEGUI 0.8.x
在OIS::Keyevent 和 CEGUI::Key::Scan之间的转化,此处并不清晰。也许存在一个我(作者)并不知晓的,更好的方式。

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

在得到了系统对象后,我们需要做两件事。第一,将键按下事件注入到CEGUI;第二,注入被传递的实际字符。正确地注入字符非常重要,因为当使用一个非英文键盘时,被注入的键按下事件,可能不总是引起希望的结果。考虑到Unicode支持,因而injectChar被设计。
现在我们需要注入键释放事件到系统中。找到keyReleased函数,并且添加以下内容替换原有内容:

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

CEGUI 0.8.x

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

注意,我们不需要注入一个字符释放事件;仅要求键释放事件。

5.5 转化并注入鼠标事件

现在我们已经挖成了处理键盘输入,需要考虑鼠标输入。然而,我们有一个需要考虑的小问题。当我们注入键释放和键按下事件到CEGUI时,我们从不需要转化键。OIS和CEGUI对键盘输入,都使用相同的键编码。对鼠标按键,这却不同。在我们可以注入鼠标按键按下到CEGUI之前,我们将需要写一个转化OIS按钮编号到CEGUI按钮编号的函数。
添加以下代码到 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;
    }
}

这可以作为一个局部静态方法,就不需要在头文件中声明。
注意:为了正确编译,函数必须在被调用之前声明或定义。确保将它添加到BasicTutorial7.cpp文件的顶部,在 includes内容之后。
现在我们准备好注入鼠标事件。找到mousePressed函数并且添加以下替换原有内容:

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

CEGUI 0.8.x仅仅沿袭对键盘按下的相同结构,添加.getDefaultGUIContext() 。同样对下两个状态如此做。

这大概是不言而喻的。我们将传入的按钮编号转化,并将结果传递给CEGUI。

找到 mouseReleased 函数并且添加下面到代码中,替换已有内容:

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

最后,我们需要将鼠标移动注入到CEGUI。CEGUI::System对象有一个 injectMouseMove 函数,要求鼠标的相对运动。OIS::mouseMoved处理器在stateX.rel和state.Y.rel变量中,把鼠标的相对运动交付给我们。
找到mouseMoved函数,并且添加以下代码覆盖原有内容:

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是被微软在过去所使用的“魔法数字”,并且在现代系统中非常常见。OIS使用了相同的数字,其它想GLUT则不是。百度它来学习更多吧。
就是这样了。现在CEGUI被完整地设置了并且接受鼠标和键盘事件。运行它,会显示用户的鼠标光标,但为了退出你需要按下Alt+F4。
注意:
在0.8.3版本,你无法看到用户鼠标光标,直到你创建并且显示一个表单(如下文)。

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

以及另外一个问题:在光标可见之前,你必须稍微动一动。目前我并不知道这个问题的原因。希望有人能告诉我。

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.

CEGUI(Crazy Eddie’s GUI http://www.cegui.org.uk)是一个自由免费的GUI库,基于LGPL协议,使用C++实现,完全面向对象设计。CEGUI开发者的目的是希望能够让游戏开发人员从繁琐的GUI实现细节中抽身出来,以便有更多的开发时间可以放在游戏性上。 CEGUI的渲染需要3D图形API的支持,如OpenGL或Direct3D。另外,使用更高级的图形库也是可以的,比如OGRE、Irrlicht和RenderWare等,关键需求可以简化为二点: 纹理(Texture)的支持直接写屏(RHW的顶点格式、正交投影、或者使用shader实现) 本书截止日时,CEGUI的最新版本是0.6.0(本书的讨论也是基于此版本),本书光盘提供了SDK和全部源码的下载。 除此之外,CEGUI还同步提供了官方界面编辑器LayoutEditor和ImageSet编辑器,以方便UI和图像集的制作。作为界面编辑器,它需要系统级界面以提供编辑器操作,0.3.0版是基于MFC实现的;而在0.4.0版本以后,改为基于wxWidgets(跨平台的本地UI框架,这里的UI指Window操作系统底层,如:Windows、Unix和Mac,详见http://www.wxwidgets.org)实现。 目前将CEGUI作为游戏界面库开发的游戏已经有好多种,国内的天龙八部,巨人等游戏就是很好的例子。 CEGUI的功能是非常强大的,而且使用也非常的灵活,可以和脚本配合。可以通过配置文件自定义窗口外观。通过布局文件实现窗口布局等等特性,使得游戏的界面开发更加方便。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值