本文介紹 OGRE 3D 1.9 程序的啟動過程,即從程序啟動到3D圖形呈現,背后有哪些OGRE相關的代碼被執行。會涉及的OGRE類包括:
- Root
- RenderSystem
- RenderWindow
- ResourceGroupManager
- LogManager
- Viewport
- SceneManager
- Camera
- SceneNode
- Entity
- Light
建議在閱讀本文時參考OGRE API Reference,OGRE官方給的API Reference沒有類的協作圖,可以自己用Doxygen生成API文檔,見:Bullet的學習資源(用Doxygen生成API文檔)。
關於如何安裝OGRE和如何配置一個可以運行的OGRE HelloWorld程序見:OGRE 1.9 的第一個程序(OGRE HelloWorld程序)。
本節所有代碼如下,可以先迅速瀏覽,然后看后面詳細解釋,后面將用“啟動代碼”來指代這段代碼:
1 #include<OgreRoot.h> 2 #include<OgreRenderSystem.h> 3 #include<OgreRenderWindow.h> 4 #include<OgreConfigFile.h> 5 #include<OgreResourceGroupManager.h> 6 #include<OgreLogManager.h> 7 #include<OgreViewport.h> 8 #include<OgreSceneManager.h> 9 #include<OgreCamera.h> 10 #include<OgreLight.h> 11 #include<OgreEntity.h> 12 13 int main(int argc, char *argv[]) 14 { 15 Ogre::Root* mRoot; 16 Ogre::RenderWindow* mWindow; 17 Ogre::SceneManager* mSceneMgr; 18 Ogre::Camera* mCamera; 19 20 // 創建Root,在調用OGRE任何功能之前必須已經創建了Root 21 mRoot = new Ogre::Root("plugins.cfg","ogre.cfg","Ogre.log"); 22 23 // 設定 RenderSystem 24 Ogre::RenderSystem *rs = 25 mRoot->getRenderSystemByName("OpenGL Rendering Subsystem"); 26 mRoot->setRenderSystem(rs); 27 rs->setConfigOption("Full Screen", "No"); 28 rs->setConfigOption("Video Mode", "800x600 @ 32-bit colour"); 29 // 另一種方法是: if(!mRoot->showConfigDialog()) return false; 30 31 // 初始化 RenderSystem 32 mRoot->initialise(false); 33 34 // 創建 RenderWindow 35 int hWnd = 0; 36 Ogre::NameValuePairList misc; 37 misc["externalWindowHandle"] = Ogre::StringConverter::toString((int)hWnd); 38 mWindow = mRoot->createRenderWindow("Win Ogre", 800, 600, false, &misc); 39 // 上2步的另一種實現是: mWindow = mRoot->initialise(true, "Win Ogre"); 40 41 // 創建SceneManager,將渲染目標綁定到RenderWindow 42 mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC); 43 // Create one camera 44 mCamera = mSceneMgr->createCamera("PlayerCam"); 45 mCamera->setNearClipDistance(5); 46 // Create one viewport, entire window 47 Ogre::Viewport* vp = mWindow->addViewport(mCamera); 48 vp->setBackgroundColour(Ogre::ColourValue(0,0,0)); 49 // Alter the camera aspect ratio to match the viewport 50 mCamera->setAspectRatio( 51 Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight())); 52 53 // 加載資源,該歩不能早於RenderSystem的初始化和RenderWindow的創建 54 // 如果使用OverlaySystem,該歩也不能早於OverlaySystem的創建 55 Ogre::ConfigFile cf; cf.load("resources.cfg"); 56 Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); 57 Ogre::String secName, typeName, archName; 58 while( seci.hasMoreElements() ){ 59 secName = seci.peekNextKey(); 60 Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext(); 61 Ogre::ConfigFile::SettingsMultiMap::iterator i; 62 for( i=settings->begin(); i!=settings->end(); ++i ){ 63 typeName = i->first; 64 archName = i->second; 65 Ogre::ResourceGroupManager::getSingleton(). 66 addResourceLocation(archName, typeName, secName); 67 } 68 } 69 Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); 70 71 // 構造及設置場景 72 mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE); 73 mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f)); 74 75 Ogre::Entity* entNinja = mSceneMgr->createEntity("entNinja", "ninja.mesh"); 76 Ogre::SceneNode* nodeNinja = mSceneMgr->createSceneNode("nodeNinja"); 77 mSceneMgr->getRootSceneNode()->addChild(nodeNinja); 78 nodeNinja->attachObject(entNinja); 79 Ogre::Entity* entSphere = mSceneMgr->createEntity("entSphere", "sphere.mesh"); 80 Ogre::SceneNode* nodeSphere = mSceneMgr->createSceneNode("nodeSphere"); 81 mSceneMgr->getRootSceneNode()->addChild(nodeSphere); 82 nodeSphere->attachObject(entSphere); 83 nodeNinja->setPosition(-50,-100,0); 84 nodeSphere->translate(50,0,100); 85 Ogre::Light* pointLight1 = mSceneMgr->createLight("pointLight1"); 86 pointLight1->setType(Ogre::Light::LT_POINT); 87 pointLight1->setDiffuseColour(Ogre::ColourValue::White); 88 pointLight1->setSpecularColour(Ogre::ColourValue::White); 89 pointLight1->setPosition(-400,200,-200); 90 91 mCamera->setPosition(Ogre::Vector3(0,0,-250)); 92 mCamera->lookAt(Ogre::Vector3(0,0,0)); 93 94 // 渲染循環 95 Ogre::LogManager::getSingleton().logMessage(">>Rendering"); 96 mRoot->startRendering(); 97 98 // 釋放資源,目前只需釋放Root 99 delete mRoot; 100 101 return 0; 102 }
運行結果截圖:
1. 啟動過程概覽
我們概要地看OGRE的啟動,OGRE WIKI Basic Tutorial 6: The Ogre Startup Sequence中摘出下面這段,注意它和上面的代碼(“啟動代碼”)是有差別的,各步驟的順序不同:
The basic Ogre life cycle looks like this:
- Create the Root object.
- Define the resources that Ogre will use.
- Choose and set up the RenderSystem (that is, DirectX, OpenGL, etc).
- Create the RenderWindow (the window which Ogre resides in).
- Initialise the resources that you are going to use.
- Create a scene using those resources.
- Set up any third party libraries and plugins.
- Create any number of frame listeners.
- Start the render loop.
總的來說,先是初始化,最后啟動渲染循環。我將所有這些類的關系總結如下圖(不是什么UML圖,就大致理解吧):
看完后面的詳細解釋后可以回過頭來看這段,那時你就會對OGRE的啟動有個大致印象。
2. 創建Root
在調用OGRE任何功能之前,首先要實例化一個Root類,該Root實例將直接或間接指向所有其他類的實例。一個OGRE程序有且只有一個Root對象,因此Root類使用Singleton設計模式(單例模式,繼承自Singleton<Root>)。說到Singleton,OGRE的很多類都是Singleton,后面還會講的。
Root類的構造函數原型如下:
Root (const String &pluginFileName="plugins"OGRE_BUILD_SUFFIX".cfg", const String &configFileName="ogre.cfg", const String &logFileName="Ogre.log")
其中OGRE_BUILD_SUFFIX宏在Release下定義為空,Debug下定義為"_d"。三個參數是三個文件名。
pluginFileName是插件配置文件,該文件指示OGRE要加載哪些插件,一個plugins.cfg文件的例子如下,其中#表示注釋:
# Defines plugins to load # Define plugin folder PluginFolder=. # Define plugins # Plugin=RenderSystem_Direct3D9 Plugin=RenderSystem_GL Plugin=Plugin_ParticleFX Plugin=Plugin_BSPSceneManager Plugin=Plugin_CgProgramManager Plugin=Plugin_PCZSceneManager Plugin=Plugin_OctreeZone Plugin=Plugin_OctreeSceneManager
configFileName文件設置渲染系統(OpenGL或者Direct3D)及其參數,如抗鋸齒采樣數(FSAA),一個針對OpenGL驅動的配置文件ogre.cfg例子如下:
Render System=OpenGL Rendering Subsystem [OpenGL Rendering Subsystem] Colour Depth=32 Display Frequency=N/A FSAA=8 Fixed Pipeline Enabled=Yes Full Screen=No RTT Preferred Mode=FBO VSync=No VSync Interval=1 Video Mode=1024 x 768 sRGB Gamma Conversion=No
logFileName文件是OGRE程序的日志文件,在OGRE程序可以插入寫日志的代碼,日志文件方便對OGRE程序的調試。向日志文件寫入信息的代碼的一個例子如下:
Ogre::LogManager::getSingleton().logMessage(">>Rendering");
這里的LogManager是另一個使用Singleton設計模式的類,這種類使用靜態方法getSingleton獲取全局唯一的類實例。
“啟動代碼”中創建Root對象的代碼在第21行:
mRoot = new Ogre::Root("plugins.cfg","ogre.cfg","Ogre.log");
3. 設定RenderSystem,初始化
RenderSystem類對渲染系統(底層的OpenGL或Direct3D)進行抽象,它相當於是執行渲染的設備。給 Root 添加一個RenderSystem實例的最簡單方式是調用Ogre::Root:: showConfigDialog方法,運行時系統將彈出如下對話框,讓用戶選擇要使用的圖形驅動,以及相應的參數:
if(!mRoot->showConfigDialog()) return false;
我們在這個對話框所做的設置被記錄在ogre.cfg文件中(見上面第2節)。也可以不用對話框,而在程序中設置,也就是說在程序中設置我們在對話框所選的項:
Ogre::RenderSystem *rs = mRoot->getRenderSystemByName("OpenGL Rendering Subsystem"); mRoot->setRenderSystem(rs); rs->setConfigOption("Full Screen", "No"); rs->setConfigOption("Video Mode", "800x600 @ 32-bit colour");
“啟動代碼”使用的是后者,代碼在第24-27行。
如果不想每次都彈出對話框選擇渲染系統,可以用如下代碼:
if( !(mRoot->restoreConfig() || mRoot->showConfigDialog()) ) return false;
restoreConfig方法讀入ogre.cfg文件來代替對話框設置,還記得C/C++邏輯運算表達式求值的短路性質吧,如果mRoot->restoreConfig()返回true(存在ogre.cfg文件),mRoot->showConfigDialog()將不被執行。
RenderSystem對象創建后需要初始化,Ogre::Root::initialise(bool, const String, const String)方法就是初始化root的RenderSystem的,如果第一個bool參數為true,將自動創建窗口,“啟動代碼”沒有這樣做,在第31行:
mRoot->initialise(false);
另外還要說的是,OGRE作為一個跨平台的高層3D圖形庫,對圖形系統進行了高度抽象,這種抽象使用戶不需要關心底層技術(如OpenGL或Direct3D、win32或Xwindow),但程序的執行必然會用到底層功能(如具體渲染任務必然是OpenGL或Direct3D執行)。OGRE(或者其他很多開源程序庫)是這樣做到這一點的:用戶使用基類(如RenderSystem和RenderWindow)接口和OGRE進行交互,代碼執行時程序自動根據系統配置調用相應子類的實現來執行命令(這得益於面向對象的繼承性和多態性)。RenderSystem類的繼承圖如下:
對於我們,使用的是OpenGL圖形驅動,所以到程序執行時,實際使用的是GLRenderSystem的實現。其實RenderSystem壓根就是個抽象類,不能被實例化。
4. 創建 RenderWindow
RenderWindow是對窗口的抽象,該窗口用來顯示渲染結果(對於離線渲染或渲染到紋理則不需要窗口)。創建窗口最簡單的方法是在調用Ogre::Root::initialise方法時傳入true作為第一個參數:
mWindow = mRoot->initialise(true, "Win Ogre");
但“啟動代碼”為了代碼的清晰,使用了手動創建RenderWindow的方法:
int hWnd = 0; Ogre::NameValuePairList misc; misc["externalWindowHandle"] = Ogre::StringConverter::toString((int)hWnd); mWindow = mRoot->createRenderWindow("Win Ogre", 800, 600, false, &misc);
注意上面使用的NameValuePairList類是用來構造參數的,你可能發現了,OGRE的很多參數都使用string數據類型。
5. 創建SceneManager,將渲染目標綁定到RenderWindow
SceneManager類管理OGRE的場景圖形(Scene Graph),《Ogre 3D 1.7 Beginner's Guide》的Chapter 6中將SceneManager的功能總結為兩個方面:
- 管理Camera, SceneNode, Entity, Light等場景中的對象,作為Factory提供create方法如createEntity(), createLight()(也負責釋放它們);
- 管理場景圖形,包括維護場景樹的可用性,計算節點的Transform信息,隱藏面剔除(Culling)。
SceneManager不是Singleton,可以從Root創建一個(或多個)SceneManager,“啟動代碼”的第41行創建了一個普通類型的SceneManager:
mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
有了SceneManager,就可以從這個Factory創建場景的對象了,主要是Camera, SceneNode, Entity, Light,構建場景(構建場景樹)留到后面說,這里說Camera和RenderWindow的對應關系。
Camera是場景到窗口輸出的媒介,負責將3D場景映射到2D窗口,這個映射涉及到另一個類Viewport,Viewport將Camera對場景的“拍攝”結果“貼”到窗口的全部可繪制區域的一個矩形部分。一個Root可以有多個SceneManager,一個SceneManager中也可以有多個Camera,但每個Camera都需要一個Viewport對應到窗口的一個矩形區域。現在你應該知道怎樣把一個場景的不同視角,或者多個場景繪制到一個窗口的不同區域了吧。
“啟動代碼”中創建Camera的代碼在第43行:
mCamera = mSceneMgr->createCamera("PlayerCam"); mCamera->setNearClipDistance(5);
其中也設置了Camera的近裁剪面。“啟動代碼”中創建建Viewport的代碼在隨后的第46行:
// Create one viewport, entire window Ogre::Viewport* vp = mWindow->addViewport(mCamera); vp->setBackgroundColour(Ogre::ColourValue(0,0,0)); // Alter the camera aspect ratio to match the viewport mCamera->setAspectRatio( Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
其中也設置了Viewport背景顏色和Camera長寬比例,addViewport是RenderWindow的方法,並以Camera為參數,這樣就把RenderWindow和Camera聯系起來了,正如我們所分析的。另外addViewport方法還有其他參數用來指定Viewport在窗口的哪一區域,上述代碼使用了缺省參數,即將Viewport對應到整個窗口。
同第4節最后說的,RenderWindow是抽象類,具體的和窗口相關的功能實際是由子類實現的,在windows上,這個子類是Win32Window。
6. 加載資源
OGRE的資源文件是OGRE的一個特色,最常見的資源文件莫過於Mesh(.mesh)和Material(.material)了,注意Mesh是一個可渲染物體,而不僅僅是一個網格,Material定義可渲染物體除幾何信息外的其他所有屬性,可以是而不限於顏色、紋理、着色器什么的。
資源文件的一個好處就是當修改了物體的外觀等信息之后,不需要重新編譯程序,如果將物體的頂點數據什么的寫在代碼里那就當然要重新編譯啦。資源文件的缺點,程序在啟動時要對資源文件進行解析(分析腳本),這增加了程序啟動時間,這也是HelloWorld程序需要好幾秒之后才能看見圖形的原因。另一個缺點,對於初學者來說,最初可能就是想畫一個長方體,但在OGRE里,你就需要創建Mesh資源。當然啦,OGRE作為面向而不限於3D游戲的3D引擎,強大的資源管理能力可以大大提高開發效率,應當說,正是資源文件的龐雜換來了程序代碼的簡潔。
有關OGRE對資源文件處理的細節見:Resources and ResourceManagers,要使用一個程序外定義(即腳本定義)的資源,需要:
- 用ResourceGroupManager::addResourceLocation方法添加資源文件所在目錄;
- 用ResourceGroupManager::declareResource方法聲明(declare)資源,可選的;
- 用ResourceGroupManager::initialiseResourceGroup或ResourceGroupManager::initialiseAllResourceGroups方法初始化所添加目錄中的資源文件腳本;
- 默認下,資源文件的數據直到該資源使用時才被加載,如一個紋理的圖片並不是在紋理定義時加載,而是在紋理被首次使用時加載至內存,也可以手動調用ResourceGroupManager::loadResourceGroup加載。
上面的第一步在“啟動代碼”中對應代碼如下,位於第54-67行:
Ogre::ConfigFile cf; cf.load("resources.cfg"); Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); Ogre::String secName, typeName, archName; while( seci.hasMoreElements() ){ secName = seci.peekNextKey(); Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext(); Ogre::ConfigFile::SettingsMultiMap::iterator i; for( i=settings->begin(); i!=settings->end(); ++i ){ typeName = i->first; archName = i->second; Ogre::ResourceGroupManager::getSingleton(). addResourceLocation(archName, typeName, secName); } }
其中"resources.cfg"是資源文件名字,文件內容如下(為了簡潔,刪減了一些):
# Resources required by the sample browser and most samples. [Essential] Zip=../../media/packs/SdkTrays.zip Zip=../../media/packs/profiler.zip FileSystem=../../media/thumbnails # Common sample resources needed by many of the samples. # Rarely used resources should be separately loaded by the # samples which require them. [Popular] FileSystem=../../media/fonts FileSystem=../../media/models Zip=../../media/packs/cubemap.zip Zip=../../media/packs/cubemapsJS.zip [General] FileSystem=../../media # Materials for visual tests [Tests] FileSystem=../../media/../../Tests/Media
Ogre::ConfigFile是一個資源配置文件解析的輔助類,類似於XML解析,和代碼對應,Essential、Popular、Popular是secName,這是OGRE為方便對資源進行管理而分的組,每個settings的格式為:typeName= archName(參數類型=參數值)。
注意下面這句代碼:
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
OGRE有很多xxxManager類,它們負責管理特定事物,如ResourceGroupManager提供對資源組的操作,LogManager提供寫日志文件功能,SceneManager管理場景圖形等等。這些Manager中的很多,比如LogManager和ResourceGroupManager使用Singleton設計模式,可以調用靜態方法getSingleton獲取全局唯一的實例。但SceneManager不是單例模式的,因為一個Root可以有多個場景圖形(場景樹)。
“啟動代碼”中declare資源的代碼如下,在第68行:
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
這里采取的是簡單粗暴的方式,解析資源目錄的所有腳本,怪不得程序啟動后要等那么久了。
注意declare資源有時不能進行的太早,例如,不能早於RenderSystem的初始化和RenderWindow的創建,如果使用OverlaySystem,也不能早於OverlaySystem的創建。原因同第3節最后分析的,因為資源的解析具體是由子類實現的,在沒有確定使用的是RenderSystem和RenderWindow的哪個子類前,不能確定使用哪個解析資源的子類,例如,RenderSystem的不同子類GLRenderSystem或D3D9RenderSystem,使用的紋理解析的類不同,如下圖:
截止目前你應該了解plugins.cfg、ogre.cfg、Ogre.log、resources.cfg文件的作用了吧。
7. 構造場景樹
目前大多數3D圖形庫采用了場景圖形(Scene Graph)技術,即用場景樹來組織場景的所有物體,場景樹的節點可分為兩種:分支節點和葉節點。分支節點SceneNode(繼承自Node)主要負責空間位置變化,葉節點可以為:Entity(可繪制實體),Light,Camera等。關於場景樹,最需要了解的是,葉節點對象在世界坐標中的最終位置是由父節點級聯決定的。一個典型的場景樹如下圖:
Entity3的世界坐標由Node5、Node4、Node2聯合決定(世界坐標計算方式可以修改)。每個Node都有一些空間變換方法:setPosition、setOrientation、setScale、translate、rotate、scale,其中前三個是覆蓋式修改,后三個是增量式修改。用Ogre::Node::addChild方法連接兩個Node,用Ogre::SceneNode::attachObject方法連接Node和葉節點。上圖有一個容易混淆的地方:Light1是不是只作用於Node4子樹呢,答案是否定的,Light1作用於整個場景樹,Camera1也是類似的。Light和Camera總是作用於整個場景樹,其上級Node只起到對其世界坐標進行變換的作用。
注意,一個可用的場景樹不能有循環路徑,如下圖的場景樹,OGRE程序運行時會拋出異常:
可以調用Ogre::SceneManager::setShadowTechnique方法設置陰影,Ogre::SceneManager::setSkyBox方法設置天空,Ogre::SceneManager::setFog方法設置霧效果。
“啟動代碼”構建了一個簡單的場景,代碼在第71-91行:
mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE); mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f)); Ogre::Entity* entNinja = mSceneMgr->createEntity("entNinja", "ninja.mesh"); Ogre::SceneNode* nodeNinja = mSceneMgr->createSceneNode("nodeNinja"); mSceneMgr->getRootSceneNode()->addChild(nodeNinja); nodeNinja->attachObject(entNinja); Ogre::Entity* entSphere = mSceneMgr->createEntity("entSphere", "sphere.mesh"); Ogre::SceneNode* nodeSphere = mSceneMgr->createSceneNode("nodeSphere"); mSceneMgr->getRootSceneNode()->addChild(nodeSphere); nodeSphere->attachObject(entSphere); nodeNinja->setPosition(-50,-100,0); nodeSphere->translate(50,0,100); Ogre::Light* pointLight1 = mSceneMgr->createLight("pointLight1"); pointLight1->setType(Ogre::Light::LT_POINT); pointLight1->setDiffuseColour(Ogre::ColourValue::White); pointLight1->setSpecularColour(Ogre::ColourValue::White); pointLight1->setPosition(-400,200,-200); mCamera->setPosition(Ogre::Vector3(0,0,-250)); mCamera->lookAt(Ogre::Vector3(0,0,0));
8. 渲染循環
調用Root::startRendering方法進入渲染循環,渲染結束釋放Root:
Ogre::LogManager::getSingleton().logMessage(">>Rendering"); mRoot->startRendering(); // 釋放資源,目前只需釋放Root delete mRoot;
其中使用LogManager這個Singleton類的功能向日志文件中寫入了信息。
startRendering函數實現如下:
void Root::startRendering(void) { assert(mActiveRenderer != 0); mActiveRenderer->_initRenderTargets(); // Clear event times clearEventTimes(); // Infinite loop, until broken out of by frame listeners // or break out by calling queueEndRendering() mQueuedEnd = false; while( !mQueuedEnd ) { //Pump messages in all registered RenderWindow windows WindowEventUtilities::messagePump(); if (!renderOneFrame()) break; } }
Root::renderOneFrame方法代碼如下:
bool Root::renderOneFrame(void) { if(!_fireFrameStarted()) return false; if(!_updateAllRenderTargets()) return false; return _fireFrameEnded(); }
也可以自行構造渲染循環,這樣就可以解決“啟動代碼”點擊關閉窗口程序也不退出的問題了:
while(true) { // Pump window messages for nice behaviour Ogre::WindowEventUtilities::messagePump(); if(mWindow->isClosed()) { return false; } // Render a frame if(!mRoot->renderOneFrame()) return false; }
9. 總結
本文要點總結如下:
- OGRE程序總是從創建Root實例開始;
- OGRE的很多xxxManager類使用了Singleton設計模式,可以調用類的靜態方法getSingleton來獲取全局唯一的類實例,如:ResourceGroupManager、LogManager、TextureManager、MeshManager等等,但SceneManager不是;
- OGRE對圖形系統進行了高度抽象,用戶使用基類接口和OGRE交互,程序執行時會自動根據系統配置調用特定子類的實現,如RenderSystem和RenderWindow;
- OGRE的場景數據用場景圖形(Scene Graph)來組織,其本質是樹(Tree),由SceneManager來組織和管理;
- 每個Camera通過一個Viewport映射到窗口的一個矩形部分(當然也可以渲染到紋理);
- OGRE的資源文件是其一大特色,資源需要特定程序加載到執行期間的程序;
- OGRE采用配置文件,本文涉及的有plugins.cfg、ogre.cfg、Ogre.log、resources.cfg文件,你應該清楚它們的作用了;
- 場景樹的可用性要求場景樹不能有循環。
好了,關於OGRE的基本啟動過程你應該了解的吧,本文並沒涉及WindowEventListener、FrameListener等一些事件的處理,也沒有涉及鼠標鍵盤輸入,甚至,“啟動代碼”運行起來后關閉窗口都不能結束程序,這些留到以后再講吧。
參考文獻:
OGRE WIKI Basic Tutorial 6: The Ogre Startup Sequence
OGRE WIKI: Resources and ResourceManagers
Ogre 3D 1.7 Beginner's Guide (Felix Kerger, 2010)
OGRE API Reference(OGRE SDK下載包中有離線版本)
OGRE 1.9 的第一個程序(OGRE HelloWorld程序)