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

posted @ 2015-08-06 23:55  天天不在  阅读(3904)  评论(2编辑  收藏  举报