• 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 解析 .  

  • 相关阅读:
    第十九天:类和对象
    第十五天:模块
    十四天:匿名函数
    十四天作业
    第十三天:迭代器、递归
    十二天:闭包和装饰器
    一个炒鸡简单的购物车
    十一天
    第十天
    第十天作业
  • 原文地址:https://www.cnblogs.com/zhouxin/p/4701481.html
Copyright © 2020-2023  润新知