天天看點

OGRE啟動順序[學習筆記]

#include <Ogre.h>

#include <OIS/OIS.h>

#include <CEGUI/CEGUI.h>

#include <OgreCEGUIRenderer.h>

using namespace Ogre;

class ExitListener : public FrameListener

{

public:

ExitListener(OIS::Keyboard *keyboard)

: mKeyboard(keyboard)

{

}

bool frameStarted(const FrameEvent& evt)

{

mKeyboard->capture();

return !mKeyboard->isKeyDown(OIS::KC_ESCAPE);

}

private:

OIS::Keyboard *mKeyboard;

};

class Application

{

public:

void go()

{

/************************************************************************

1、建立Root對象。

2、定義Ogre将要使用的資源。

3、選擇并設定渲染系統(即DirectX, OpenGL等)。

4、建立渲染視窗(Ogre所處的視窗)。

5、初始化你要使用的資源。

6、用這些資源來建立一個場景。

7、設定第三方庫或插件。

8、建立一些幀監聽器。

9、啟動渲染循環

************************************************************************/

createRoot();

defineResources();

setupRenderSystem();

createRenderWindow();

initializeResourceGroups();

setupScene();

setupInputSystem();

setupCEGUI();

createFrameListener();

startRenderLoop();

}

~Application()

{

/************************************************************************

最後一件事是,當程式終止時,我們要對所建立的所有對象進行清理。為止,我們

将按照與建立時相反的順序來删除或銷毀這些對象。我們從OIS開始下手,它有一個

專門的方法來銷毀它的對象。添加如下代碼:

************************************************************************/

mInputManager->destroyInputObject(mKeyboard);

OIS::InputManager::destroyInputSystem(mInputManager);

/************************************************************************

現在我們來清理CEGUI,隻要删除對象即可:

************************************************************************/

delete mRenderer;

delete mSystem;

/************************************************************************

最後,我們要删除Root和FrameListener對象。當删除Root對象時,我們建立的其它

對象(SceneManager, the RenderWindow等)也會一并删除。

************************************************************************/

delete mListener;

delete mRoot;

/************************************************************************

好了! 你現在可以編譯并運作你的程式了,雖然你将隻能看見一個黑屏,因為我們

并沒有往場景添加任何東西。如果在編譯裡遇到連結問題,確定CEGUIBase_d.lib

和OgreGUIRenderer_d.lib添加到了連結器的輸入裡(這是debug模式的,如果是release

模式,去掉_d)。現在你應該對Ogre的啟動過程比較熟悉了,可以抛開示例架構而使

用其它的了。

************************************************************************/

}

private:

Root *mRoot;

OIS::Keyboard *mKeyboard;

OIS::InputManager *mInputManager;

CEGUI::OgreCEGUIRenderer *mRenderer;

CEGUI::System *mSystem;

ExitListener *mListener;

void createRoot()

{

/************************************************************************

1、建立Root對象

Root的構造函數需要三個參數。第一個是插件配置檔案的名稱和路徑。

第二個是Ogre配置檔案的路徑(它告訴Ogre關于顯示卡、顯示設定等資訊)。

最後一個是日志檔案的名稱和路徑。因為我們不需要修改任何一個屬性,是以用預設的。

************************************************************************/

mRoot = new Root();

}

void defineResources()

{

/************************************************************************

2、資源

注意: 你最好在SDK的bin/release目錄裡找到resources.cfg,看看它的内容是很有幫助的。

下面我們要定義程式将要使用的資源,包括紋理、模型、腳本等等。請記住,你必須預先定

義好你的資源,在Ogre能使用它們之前,你還必須對它進行初始化。在這一步裡,我們來定義

所有程式可能使用的資源。為止,我們把每一個資源所在的檔案夾添加到ResourceGroupManager。

************************************************************************/

String secName, typeName, archName;

ConfigFile cf;

cf.load("resources.cfg");

/************************************************************************

這裡使用了一個Ogre的ConfigFile類來解析"resources.cfg"裡的所有的資源,

但并不是把它們裝入到Ogre(你必須手工添加)。記住,你可以自由地使用你自己的

檔案格式和解析器,隻需要用你自己的解析器來替換ConfigFile的。裝入資源的方

式并不十分重要,隻要你能把資源添加到ResourceGroupManager。現在我們解析好

了cfg檔案,還需要把各部分添加到ResourceGroupManager中。以下代碼啟動一個循

環:

************************************************************************/

ConfigFile::SectionIterator seci = cf.getSectionIterator();

while (seci.hasMoreElements())

{

/************************************************************************

每次循環裡,我們再循環一次,提取它裡面所有的内容:

************************************************************************/

secName = seci.peekNextKey();

ConfigFile::SettingsMultiMap *settings = seci.getNext();

ConfigFile::SettingsMultiMap::iterator i;

/************************************************************************

添加部件名稱(那一組資源的),資源類型(zip,檔案夾等等),以及資源本身的檔案

名,給ResourceGroupManager:

************************************************************************/

for (i = settings->begin(); i != settings->end(); ++i)

{

typeName = i->first;

archName = i->second;

ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);

}

}

}

void setupRenderSystem()

{

/************************************************************************

3、建立渲染系統

接下來,我們需要選擇一個渲染系統(在Windows機器上通常是DirectX或者OpenGL),

然後配置它。大多數Demo程式使用的是一個Ogre配置對話框,這是一個很好的一個東

東。Ogre提供了一種儲存使用者設定的方法,意味着除了第一次需要設定外,以後都不

需要了。并添加以下代碼:

************************************************************************/

if (!mRoot->restoreConfig() && !mRoot->showConfigDialog())

throw Exception(52, "User canceled the config dialog!", "Application::setupRenderSystem()");

/************************************************************************

在if語句裡的第一部分,嘗試恢複這個config檔案。如果函數傳回false,意味着文

件不存在,則應該顯示配置對話框,也就是if語句的第二部分。如果仍然傳回false

,意味着使用者取消了配置對話框(也就是他們想退出程式)。在這個例子裡,抛出了一

個異常,但實際上簡單地傳回false且關閉應用程式,這樣可能更好。由于 restoreConfig

和 showConfigDialog 也可能抛出異常,儲存這些真正的異常可能更好。然而,這将

增加了教程的不必要的複雜度,我在這裡隻使用了一個異常。

如果你在Ogre的啟動過程中捕獲了一個異常,最好在catch裡删除這個ogre.cfg檔案。

因為他們在配置對話框裡進行的設定可能導緻了問題的發生,是以他們需要更改它。

你也可以不使用它,關閉配置對話框可以節省開發時間,因為你不必每次程式運作時

确認這些顯示設定。

如果你不打算使用Ogre的配置對話框,你需要手動地設定渲染系統。以下是一個基本

的例子:

************************************************************************/

// Do not add this to the application

RenderSystem *rs = mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");

// or use "OpenGL Rendering Subsystem"

mRoot->setRenderSystem(rs);

rs->setConfigOption("Full Screen", "No");

rs->setConfigOption("Video Mode", "1440 x 900 @ 32-bit colour");

}

void createRenderWindow()

{

/************************************************************************

4、建立渲染視窗

目前選擇了一個渲染系統,還需要一個渲染Ogre的視窗。實際上有許多種方式來實作

,但這裡隻介紹兩種。

************************************************************************/

mRoot->initialise(true, "Tutorial Render Window");

/************************************************************************

第一個參數表示是否讓Ogre為你建立一個渲染視窗。否則,你可以自己建立一個渲

染視窗,通過使用win32 API、wxWidgets或其它Windows/Linux的GUI系統。關于在

Windows下的一個簡單例子是這樣:

************************************************************************/

Do not add this to the application

//mRoot->initialise(false);

//HWND hWnd = 0; // Get the hWnd of the application!

//NameValuePairList misc;

//misc["externalWindowHandle"] = StringConverter::toString((int)hWnd);

//RenderWindow *win = mRoot->createRenderWindow("Main RenderWindow", 800, 600, false, &misc);

/************************************************************************

在這裡你仍然使用Root::initialise,第一個參數設定成了false。然後,你必須獲

取你希望Ogre渲染的視窗的句柄。你如何取得它,完全決定于你用來建立視窗的GUI

工具箱(在Linux下我估計這有一點差別)。你擁有了它之後,你通過NameValuePairList

handle把這個句柄賦予"externalWindowHandle"。Root::createRenderWindow方法被

用來從你建立的視窗來建立RenderWindow對象。想了解更多,參考這個方法的API文

檔。

************************************************************************/

}

void initializeResourceGroups()

{

/************************************************************************

5、初始化資源

現在我們建立了Root對象、渲染系統、以及渲染視窗,繼續。接下來是初始化将要使

用的資源。從mesh到腳本,所有的東西,在某一時刻,我們隻用到這些資源其中的一

小部分。為了減少記憶體消耗,我們可以隻加載正在使用的資源。為止,我們把資源分

解成各種部分,隻在運作時初始化它們。在本課裡,不将詳細介紹。其它的地方有一

個專門介紹資源的教程。初始化資源之前,我們應該設定紋理mipmap的預設值。添加

如下代碼:

************************************************************************/

TextureManager::getSingleton().setDefaultNumMipmaps(5);

ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

}

void setupScene()

{

/************************************************************************

6、建立場景

你應該了解在把各種東西添加到場景之前,你要做三件事:建立場景管理器

(SceneManager)、建立錄影機(Camera)、建立視口(Viewport)。在setupScene

方法裡添加如下代碼:

************************************************************************/

SceneManager *mgr = mRoot->createSceneManager(ST_GENERIC, "Default SceneManager");

Camera *cam = mgr->createCamera("Camera");

Viewport *vp = mRoot->getAutoCreatedWindow()->addViewport(cam);

/************************************************************************

如果需要的話,可以建立多個場景管理器、多個錄影機,但當真正打算使用錄影機

把事物渲染到螢幕上時,請確定已經為它添加了視口中。為止,要借助RenderWindow

類,它在“建立渲染視窗”一節裡被建立。由于我們沒有這個對象的指針,是以我們

通過Root::getAutoCreatedWindow方法來擷取它。

這三件事完了以後,你可以盡情地往你的場景裡添加物體了。

************************************************************************/

}

void setupInputSystem()

{

/************************************************************************

7、設定第三方庫

OIS

雖然在OGRE裡,OIS不是唯一的選擇,但它是最好的之一。我來簡單介紹一下OIS如何

在程式裡啟動。若真想要使用這個庫,請參考這個教程,以及OIS自身的文檔。

設定無緩沖輸入

OIS使用一個統一的InputManager,它比較難配置,但一旦正确地建立之後,非常好使

用。實際上,它隻是需要Ogre渲染視窗的句柄。幸好,由于我們使用的是自動建立的窗

口,Ogre使之簡化了。添加如下代碼:

************************************************************************/

size_t windowHnd = 0;

std::ostringstream windowHndStr;

OIS::ParamList pl;

RenderWindow *win = mRoot->getAutoCreatedWindow();

win->getCustomAttribute("WINDOW", &windowHnd);

windowHndStr << windowHnd;

pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

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

/************************************************************************

這樣InputManager就建好了,但為了從鍵盤、滑鼠、或是搖桿中獲得輸入,你還必

須建立這些對象:

************************************************************************/

try

{

mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard, false));

//mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject(OIS::OISMouse, false));

//mJoy = static_cast<OIS::JoyStick*>(mInputManager->createInputObject(OIS::OISJoyStick, false));

}

catch (const OIS::Exception &e)

{

throw new Exception(42, e.eText, "Application::setupInputSystem");

}

/************************************************************************

我把Mouse和Joystick對象注釋掉了,因為這裡我們不使用,但它們就是這樣建立的。

InputManager::createInputObject的第二個參數是指是否使用帶緩沖輸入(在以前的

教程裡介紹過)。把第二個參數設定成false,建立了一個無緩沖的輸入對象,我們這

裡就使用這個。

************************************************************************/

}

void setupCEGUI()

{

/************************************************************************

CEGUI

CEGUI是直接整合到Ogre裡的一個非常靈活的GUI庫。在這裡不使用CEGUI的任何功能

,但我還是來簡單介紹一下它的設定。CEGUI需要RenderWindow和SceneManager以供

渲染。

************************************************************************/

SceneManager *mgr = mRoot->getSceneManager("Default SceneManager");

RenderWindow *win = mRoot->getAutoCreatedWindow();

// CEGUI setup

mRenderer = new CEGUI::OgreCEGUIRenderer(win, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mgr);

mSystem = new CEGUI::System(mRenderer);

/************************************************************************

就這樣,你就能使用CEGUI了。如果你程式中途你改變了SceneManager,你必須通知

CEGUI應該渲染到一個新的SceneManager。

為止,使用OgreCEGUIRenderer::setTargetSceneManager就行了。

************************************************************************/

// Other CEGUI setup here.

}

void createFrameListener()

{

/************************************************************************

8、渲染循環以及最後的工作

幀監聽

在我們開始渲染循環,并讓程式運作之前,我們還需要添加幀監聽器。請注意我已經

建立一個非常簡單的幀監聽器,名為ExitListener,它等待ESC鍵被按下,以退出程

序。在你的程式裡,我可能需要更多的幀監聽器,來做更複雜的事情。看一下這個

ExitListener,確定了解它的流程。

************************************************************************/

mListener = new ExitListener(mKeyboard);

mRoot->addFrameListener(mListener);

}

void startRenderLoop()

{

/************************************************************************

9、渲染循環

最後我們要做的是啟動Ogre的渲染循環。非常簡單,添加如下代碼:

************************************************************************/

mRoot->startRendering();

/************************************************************************

這樣程式就開始渲染,直到FrameListener傳回false。你也可以提取單個幀,并在

每幀之間做一些事情。Root::renderOneFrame渲染一幀,如何任何一個FrameListener

傳回false,它也傳回false:

************************************************************************/

Do not add this to the application

//while (mRoot->renderOneFrame())

//{

// // Do some things here, like sleep for x milliseconds or perform other actions.

//}

/************************************************************************

然而,在我看來,你應該把所有while循環裡的代碼轉移到FrameListener。我能想

到的這種模式的唯一用處就是,中途睡眠某些毫秒,進而人為地降低幀率到某一個

值。一般在FrameListener裡不會這樣做,因為它會與FrameEvent::timeSinceLastFrame

變量搞混淆。

************************************************************************/

}

};

#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)

#else

int main(int argc, char **argv)

#endif

{

try

{

Application app;

app.go();

}

catch(Exception& e)

{

#if OGRE_PLATFORM == PLATFORM_WIN32 || 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;

}

繼續閱讀