基礎教程九(Ogre的啟動順序)

來源:互聯網
上載者:User
介紹

在這一課裡,你將學習如何啟動OGRE,而不用樣本架構。學習完之後,你將會建立你自己的Ogre應用程式,且不使用ExampleApplication或ExampleFrameListener。

你能在這裡找到本課的代碼。當你學習本課程時,你應該逐個地往你的工程裡添加代碼,編譯並觀察結果。

準備開始

初始代碼
   #include    #include    #include    #include       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()       {           createRoot();           defineResources();           setupRenderSystem();           createRenderWindow();           initializeResourceGroups();           setupScene();           setupInputSystem();           setupCEGUI();           createFrameListener();           startRenderLoop();       }          ~Application()       {       }      private:       Root *mRoot;       OIS::Keyboard *mKeyboard;       OIS::InputManager *mInputManager;       CEGUI::OgreCEGUIRenderer *mRenderer;       CEGUI::System *mSystem;       ExitListener *mListener;          void createRoot()       {       }              void defineResources()       {       }              void setupRenderSystem()       {       }              void createRenderWindow()       {       }          void initializeResourceGroups()       {       }          void setupScene()       {       }          void setupInputSystem()       {       }          void setupCEGUI()       {       }          void createFrameListener()       {       }          void startRenderLoop()       {       }   };      #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;   }

從零開始

開始之前,讓我們站在更高的層次上看一下啟動過程是如何工作的:

  1. 建立Root對象。
  2. 定義Ogre將要使用的資源。
  3. 選擇並設定渲染系統(即DirectX, OpenGL等)。
  4. 建立渲染視窗(Ogre所處的視窗)。
  5. 初始化你要使用的資源。
  6. 用這些資源來建立一個情境。
  7. 設定第三方庫或外掛程式。
  8. 建立一些幀監聽器。
  9. 啟動渲染迴圈

本課將逐一介紹這些步驟。請注意步驟1-4必須嚴格按順序進行,而5(初始化資源)和6(建立情境)可以放在更後一點,如果你喜歡的話。但我這裡不推薦,除非你真的很瞭解你的工作順序。

啟動Ogre

建立Root對象

這個很簡單,Root對象是Ogre庫裡最核心的,在你用這個引擎做其它事情之前,你必須先建立它。找到Application::createRoot方法,添加如下代碼:

           mRoot = new Root();

Root的建構函式需要三個參數。第一個是外掛程式設定檔的名稱和路徑。第二個是Ogre設定檔的路徑(它告訴Ogre關於顯卡、顯示設定等資訊)。最後一個是記錄檔的名稱和路徑。因為我們不需要修改任何一個屬性,所以用預設的就好了。

資源

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

下面我們要定義程式將要使用的資源,包括紋理、模型、指令碼等等。請記住,你必須預先定義好你的資源,在Ogre能使用它們之前,你還必須對它進行初始化。在這一步裡,我們來定義所有程式可能使用的資源。為止,我們把每一個資源所在的檔案夾添加到ResourceGroupManager。找到defineResources方法,並添加如下代碼:

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

這個方法從config檔案添加所有的資源,但它只告訴Ogre它們在哪。但如果你打算使用它們,你還必須初始化它們。我們在後面“初始化資源”一節來介紹。

建立渲染系統

接下來,我們需要選擇一個渲染系統(在Windows機器上通常是DirectX或者OpenGL),然後配置它。大多數Demo程式使用的是一個Ogre配置對話方塊,這是一個很好的一個東東。Ogre提供了一種儲存使用者佈建的方法,意味著除了第一次需要設定外,以後都不需要了。找用setupRenderSystem方法,並添加以下代碼:

      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的配置對話方塊,你需要手動地設定渲染系統。以下是一個基本的例子:

      // 不要把這些添加到程式中      RenderSystem *rs = mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");      mRoot->setRenderSystem(rs);      rs->setConfigOption("Full Screen", "No");      rs->setConfigOption("Video Mode", "800 x 600 @ 32-bit colour");

你可以使用Root::getAvailableRenderers來瞭解有哪些渲染系統可供你的程式使用。一旦你得到了一個渲染系統,你能夠使用RenderSystem::getConfigOptions來查看有哪些選項可以提供給使用者。利用這兩個方法,你可以建立你自己的配置對話方塊。

建立渲染視窗

目前我們選擇了一個渲染系統,我們還需要一個渲染Ogre的視窗。實際上有許多種方式來實現,但這裡只介紹兩種。

如果你希望Ogre為你建立一個渲染視窗,這是相當容易的事情。找到createRenderWindow方法,這麼寫:

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

第一個參數表示是否讓Ogre為你建立一個渲染視窗。否則,你可以自己建立一個渲染視窗,通過使用win32 API、wxWidgets或其它Windows/Linux的GUI系統。關於在Windows下的一個簡單例子是這樣:

      // 別把這些添加到程式裡      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文檔。

初始化資源

現在我們建立了Root對象、渲染系統、以及渲染視窗,繼續。接下來是初始化我們將要使用的資源。從mesh到指令碼,所有的東西,在某一時刻,我們只用到這些資源其中的一小部分。為了減少記憶體消耗,我們可以只載入正在使用的資源。為止,我們把資源分解成各種部分,只在運行時初始化它們。在本課裡,我們不將詳細介紹。其它的地方有一個專門介紹資源的教程。初始化資源之前,我們應該設定紋理mipmap的預設值。找到initializeResourceGroups方法,添加如下代碼:

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

這樣程式就擁有了所有已經被初始化的資源群組以供使用。

建立情境

你應該瞭解在把各種東西添加到情境之前,你要做三件事:建立情境管理器(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方法來擷取它。

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

設定第三方庫

OIS

雖然在OGRE裡,OIS不是唯一的選擇,但它是最好的之一。我來簡單介紹一下OIS如何在程式裡啟動。若真想要使用這個庫,請參考這個教程,以及OIS自身的文檔。

設定無緩衝輸入

OIS使用一個統一的InputManager,它比較難配置,但一旦正確地建立之後,非常好使用。實際上,它只是需要Ogre渲染視窗的控制代碼。幸好,由於我們使用的是自動建立的視窗,Ogre使之簡化了。找到setupInputSystem方法,添加如下代碼:

      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, false);

這樣InputManager就建好了,但為了從鍵盤、滑鼠、或是手柄中獲得輸入,你還必須建立這些對象:

      try      {          mKeyboard = static_cast(mInputManager->createInputObject(OIS::OISKeyboard, false));          //mMouse = static_cast(mInputManager->createInputObject(OIS::OISMouse, false));          //mJoy = static_cast(mInputManager->createInputObject(OIS::OISJoyStick, false));      }      catch (const OIS::Exception &e)      {          throw Exception(42, e.eText, "Application::setupInputSystem");      }

我把Mouse和Joystick對象注釋掉了,因為這裡我們不使用,但它們就是這樣建立的。InputManager::createInputObject的第二個參數是指是否使用帶緩衝輸入(在以前的教程裡介紹過)。把第二個參數設定成false,建立了一個無緩衝的輸入對象,我們這裡就使用這個。

建立幀監聽器

不論你是否使用帶緩衝的輸入,每一幀之間你必須調用鍵盤、滑鼠或者手柄對象的捕獲方法。在本課的起始程式碼裡已經做了這些。對於無緩衝的輸入,你只需要做這些。每一幀之間,你都可以調用滑鼠鍵盤的各種方法來查詢這些對象的狀態。而對於帶緩衝的輸入,我們還需稍微地做一些事情。

為了使用帶緩衝的輸入,你要添加一個類,以處理輸入。兩件事情要做,一是實現適合的監聽介面,二是把你建立的這個類註冊成為這個事件的回調。在前面的課程裡,你能找到帶緩衝國輸入的例子,下面是這個類的代碼:

   // 這別它添加到工程   class BufferedInputHandler : public OIS::KeyListener, public OIS::MouseListener, public OIS::JoyStickListener   {   public:       BufferedInputHandler(OIS::Keyboard *keyboard = 0, OIS::Mouse *mouse = 0, OIS::JoyStick *joystick = 0)   {       if (keyboard)           keyboard->setEventCallback(this);
       if (mouse)           mouse->setEventCallback(this);
       if (joystick)           joystick->setEventCallback(this);   }
   // KeyListener   virtual bool keyPressed(const OIS::KeyEvent &arg) { return true; }   virtual bool keyReleased(const OIS::KeyEvent &arg) { return true; }
   // MouseListener   virtual bool mouseMoved(const OIS::MouseEvent &arg) { return true; }   virtual bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id) { return true; }   virtual bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id) { return true; }
   // JoystickListener   virtual bool buttonPressed(const OIS::JoyStickEvent &arg, int button) { return true; }   virtual bool buttonReleased(const OIS::JoyStickEvent &arg, int button) { return true; }   virtual bool axisMoved(const OIS::JoyStickEvent &arg, int axis) { return true; }

};

我建議您僅實現自己之後實際需要的監聽函數.

帶緩衝輸入的疑難雜症

如果你沒有如自己所希望那樣在程式中獲得輸入緩衝,那麼你需要檢查以下這些事情:

  1. 你在調用InputManager::createInputObject()函數建立輸入裝置時是否設定第二個參數(開啟輸入緩衝)為true?
  2. 你是否為每個輸入緩衝對象設定了一個setEventCallback()函數?
  3. 是否有別的類調用了setEventCallback()函數?(注意:OIS僅僅允許一個事件回調給一個對象,你不能把一個事件回調給兩個類處理.)

如果你檢查了這三個問題依然有錯誤的話,請將您的問題發表在Ogre協助論壇.

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就行了。

渲染迴圈以及最後的工作

幀監聽

在我們開始渲染迴圈,並讓程式運行之前,我們還需要添加幀監聽器。請注意我已經建立一個非常簡單的幀監聽器,名為ExitListener,它等待ESC鍵被按下,以退出程式。在你的程式裡,我可能需要更多的幀監聽器,來做更複雜的事情。看一下這個ExitListener,確保瞭解它的流程。然後把如下代碼添加到createFrameListener方法裡:

      mListener = new ExitListener(mKeyboard);      mRoot->addFrameListener(mListener);

渲染迴圈

最後我們要做的是啟動Ogre的渲染迴圈。非常簡單,找到startRenderLoop方法,並添加如下代碼:

      mRoot->startRendering();

這樣程式就開始渲染,直到FrameListener返回false。你也可以提取單個幀,並在每幀之間做一些事情。Root::renderOneFrame渲染一幀,如何任何一個FrameListener返回false,它也返回false:

      // 別把這段加到工程裡      while (mRoot->renderOneFrame())      {          // Do some things here, like sleep for x milliseconds or perform other actions.      }

然而,在我看來,你應該把所有while迴圈裡的代碼轉移到FrameListener。我能想到的這種模式的唯一用處就是,中途睡眠某些毫秒,從而人為地降低幀率到某一個值。一般在FrameListener裡不會這樣做,因為它會與FrameEvent::timeSinceLastFrame變數搞混淆。

清理

最後一件事是,當程式終止時,我們要對所建立的所有對象進行清理。為止,我們將按照與建立時相反的順序來刪除或銷毀這些對象。我們從OIS開始下手,它有一個專門的方法來銷毀它的對象。找到~Application方法,並添加如下代碼:

      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的啟動過程比較熟悉了,可以拋開樣本架構而使用其它的了。然而,為了簡單化,後面的課程將仍然使用那個樣本架構。

如果你對樣本架構中的其它部分感興趣,請參考更深入的文章。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.