Ogre 編輯器一(MyGUI+Ogre整合與主界面)


  在查看Ogre例子時,想看材質要里的紋理,着色器代碼都需要每個去查找,非常麻煩.也想看更新每個Ogre里的對象后有什么效果.然后看到Compositor組件與粒子組件時,想到能實時編輯着色器代碼實時更新渲染.

  開始想着C++做界面麻煩,用C#的winForm做,后面發現首先結合層比較麻煩,然后C#與C++一起調試也會比較麻煩,還有一些比較奇怪的異常也會麻煩.好吧,不如全用C++做,在學習能用在Ogre中的UI時,主要了解了包括Ogre自己的Overlay, CEGUI, MyGUI等等,最終選擇MyGUI,因為他小,功能全,代碼容易理解,這樣拓展起來也方便.因此最終選定Ogre1.9 + MyGUI3.2.2.這二個項目還都是跨平台的,如果有機會,后面可以嘗試移值.開發平台暫時選用VS2013.

  首先整合MyGUI到Ogre中,這部分主要是加載MyGUI的資源文件與初始化MyGUI的環境.其中初始化整個函數如下.  

        void initRoot()
        {
            String pluginsPath = fsLayer->getConfigFilePath("plugins.cfg");
            String logPath = fsLayer->getWritablePath("ogre.log");
            root = new Root(pluginsPath, fsLayer->getWritablePath("ogre.cfg"), "ogre.log");
            //root->showConfigDialog();            
            bool foundit = false;
            for (auto rs : root->getAvailableRenderers())
            {
                root->setRenderSystem(rs);
                String rname = root->getRenderSystem()->getName();
                if (rname == "OpenGL Rendering Subsystem")//"OpenGL Rendering Subsystem"
                {
                    foundit = true;
                    break;
                }
            }
            if (!foundit)
                return; //we didn't find it... Raise exception?
            //we found it, we might as well use it!
            root->getRenderSystem()->setConfigOption("Full Screen", "No");
            root->getRenderSystem()->setConfigOption("Video Mode", "1024 x 768 @ 32-bit colour");
            window = root->initialise(true, "Ogre3DX");
            Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
            allResListener = new AllResourceListener();

            this->loadUIResources();
            this->loadResources();
            this->createSceneManager();
            this->createView();
            this->createGui();
            this->createSceneRoot();
        }
InitRoot

   先初始化Root,選擇渲染系統,初始化渲染窗口,加載UI資源等.我們單獨把UI部分的資源拿出來,這樣可以先加快界面出現過程,然后在界面里用進度條等顯示加載游戲資源,這是大頭部分,后面也會修改成這種,如下是UI資源部分代碼.

        void loadUIResources()
        {
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Core", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/OgreData", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Show", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Show/Main", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Show/Panel", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Show/Other", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Show/PanelView", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Show/PropertyField", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/Themes", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().addResourceLocation("../../Media/GUI/TreeControl", "FileSystem", "MyGUI");
            ResourceGroupManager::getSingleton().initialiseResourceGroup("MyGUI");
        }
Load UI

  其中GUI/Core里放的就是MyGUI中的Media/MyGUI_Media這個目錄,這個是必需的,MyGUI下另外的幾個文件夾都是非必要的.不過在這里,我設定的是黑色風格,MyGUI_Media默認提供的不是這種,所以把原MyGUI下的Media/Tools/LayoutEditor/Themes放入我們的資源文件GUI/Themes下,另外一些文件夾后面遇到再說.

  在生成viewport與camera后,我們開始加載MyGUI,如下是初始MyGUI環境代碼.

void createGui()
        {
            ogrePlatform = new OgrePlatform();
            ogrePlatform->initialise(window, sceneMgr, "MyGUI");
            gui = new Gui();
            gui->initialise();

            std::ostringstream handleStr;
            long handle = 0;
            window->getCustomAttribute("WINDOW", &handle);

            inputMgr = new OgreViewUI::InputManager();
            inputMgr->createInput(handle);
            pointMgr = new OgreViewUI::PointerManager();
            pointMgr->createPointerManager(handle);
            pointMgr->loadPointerResources();

            MyGUI::ResourceManager::getInstance().load("FrameworkFonts.xml");
            MyGUI::ResourceManager::getInstance().load("MyGUI_DarkSkin.xml");
            MyGUI::ResourceManager::getInstance().load("MyGUI_DarkTemplate.xml");
            MyGUI::ResourceManager::getInstance().load("TreeControlSkin.xml");
            MyGUI::ResourceManager::getInstance().load("TreeControlTemplate.xml");
            MyGUI::ResourceManager::getInstance().load("AutoComplete.xml");

            MyGUI::FactoryManager& factory = MyGUI::FactoryManager::getInstance();
            std::string widgetCategory = MyGUI::WidgetManager::getInstance().getCategoryName();
            factory.registerFactory<MyGUI::TreeControl>(widgetCategory);
            factory.registerFactory<MyGUI::TreeControlItem>(widgetCategory);
            factory.registerFactory<MyGUI::AutoComplete>(widgetCategory);
        }
MyGUI Initialise

  OgrePlatform的初始化就是把對應MyGUI的RenderManager關聯在Ogre的渲染過程中,詳細說明請看我上一篇 MyGUI 解析 里有詳細介紹這個過程.而gui對象的初始化就是對內部的單例管理類初始化,初始化的過程大部分都在解析上面所說MyGUI中的Media/MyGUI_Media 中的文件.

  然后我們初始化InputManager與PointerManager這二個類,這二個類會在MyGUI下的Common/Input中提供,一個截獲鼠標與鍵盤事件,一個是管理鼠標顯示狀態.

  前面所說,Media/MyGUI_Media下文件名都是固定的,MyGUI初始化時自動會去加載對應的固定名,而非那個文件夾下的文件,我們需要自己用MyGUI提供的ResourceManger進行load,前面我們用Ogre去load,但是Ogre不能處理這些文件,但是會記錄對應文件路徑,這樣讀出對應的文件流給MyGUI去處理.然后我們注冊我們自定義的一些UI組件,上面的是樹型控件和自動完成控件.

  這樣Ogre與MyGUI就整合在一起了.然后就是主界面大致設定,如下圖所示.

  

  暫時大致分成五個部分,上面是菜單區,左邊是管理區,大致分成場景,資源等,中間上面是顯示區域,中間下面暫時空出,右邊部分是屬性區.

  在這里,我們要先定義一個主界面的Layout文件與菜單的Layout文件如下:

  

  

  注意Align,在這,我們想讓主界面占滿整個窗口,則定義為Stretch,相當於winForm中的Fill,注意這也是第一層控件,我們需要定義他的name固定為Root或是_Main,因為MyGUI給我們提供的一個基本管理Layout文件類BaseLayout有字段mMainWidget,在初始化時,檢測名為Root或_Main的Widget賦給mMainWidget,找不到則給出異常,這樣有個好處,mMainWidget能代表當前Layout的大小,如果我們調整大小,修改這個就好,並且根據子Widget的Align來調整子Widget的大小,還有Layer,前文說過,同一層的UI定義的Layer最好在同一層,不然層之間會遮擋.

  在主界面的Layout中定義划二個Widget控件在上面,一個name為MainMenuControl,Align為Top.一個name為MainControl,Align為Stretch.而在菜單界面對應的Layout中放入的是MenuBar控件在上面.

  對於菜單界面,我們直接使用Layout editor生成的代碼,如下代碼.

    ATTRIBUTE_CLASS_LAYOUT(MainMenu, "MainMenuControl.layout");
    class MainMenu :
        public wraps::BaseLayout
    {
    public:
        MainMenu(MyGUI::Widget* _parent = nullptr);
        virtual ~MainMenu();

    private:
        void mouseClick(MyGUI::Widget* sender);
    private:
        //%LE Widget_Declaration list start
        ATTRIBUTE_FIELD_WIDGET_NAME(MainMenu, mMenuMenuBar, "Menu");
        MyGUI::MenuBar* mMenuMenuBar;
        ATTRIBUTE_FIELD_WIDGET_NAME(MainMenu, mLoadMenuItem, "load");
        MyGUI::MenuItem* mLoadMenuItem;
        //%LE Widget_Declaration list end
    };

    MainMenu::MainMenu(MyGUI::Widget* _parent)
    {
        initialiseByAttributes(this, _parent);
        mLoadMenuItem->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::mouseClick);
    }

    MainMenu::~MainMenu()
    {
    }

    void MainMenu::mouseClick(MyGUI::Widget* sender)
    {

    }
MainMenu

  每個類名前面一個宏,控件字段上帶一個宏,這樣在初始化,調用initialiseByAttributes時,其實就是分析相應宏設置,類宏提供對應的Layout文件(需要注意放入Ogre加載的文件夾下,否則找不到), 控件字段提供控件對象與Layout中的子Widget對應.其實我們不要上面的宏,在初始化調用如下語句,是一個意思.

    MainMenu::MainMenu(MyGUI::Widget* _parent)
    {
        initialise("MainPane.layout");
        assignWidget(mMenuMenuBar, "Menu");
        assignWidget(mLoadMenuItem, "load");

        //initialiseByAttributes(this, _parent);
        mLoadMenuItem->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::mouseClick);
    }
MainMenu

  我們還記的主控件里放入的是二個Widget,並沒有實際對應某種控件,一般控件里放入Widget,表示實際對應是另一個我們自己定義的Layout,如這里,上面的Widget明顯對應的是我們上面所說的MainMenu.我們看下,如下把這二者關聯起來.

    class MainPane :
        public wraps::BaseLayout
    {
    public:
        MainPane(MyGUI::Widget* _parent = nullptr);        
        virtual ~MainPane();
    private:
        MainMenu* mMainMenuControl = nullptr;
        
        tools::PropertiesPanelView* properyPanel2 = nullptr;
    };

    MainPane::MainPane(MyGUI::Widget* _parent)
    {            
        initialise("MainPane.layout");
        assignBase(mMainMenuControl, "MainMenuControl");
        assignBase(properyPanel2 , "MainControl");
    }
MainPane

  在MainPane里,我們自己來寫,沒用生成的代碼,我們也就不用那些宏了,自己來初始化,和前面一樣,先initialise對應的layout文件,然后把layout對應控件名給某控件,和前面用assignWidget不同,這里我們用的是assignBase.assignWidget一般用於指定layout的template控件(也就是系統內定的控件),而assignBase一般用於把layout中的Type為Widget的控件定義成我們自定義的Layout對應類.

  MyGUI說大了,再復雜的界面都是於assignWidget與assignBase來構成,對於其內部具體實現,請看相關代碼與於我前篇 MyGUI 解析 .  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM