Ogre 編輯器三(自動生成與更新Ogre對象編輯界面)


  最開始設計這個編輯器時,其中一個要求就是能在運行過程中,通過UI來更新各對象,這樣我們就能明確每個Ogre對象更新其屬性影響的渲染效果.比如點光源,方向光源,聚光燈各屬性與效果,深度測試開啟與關閉,深度比較方式影響的效果等等.如下先看效果圖:

  這個位置沒有用上一篇天龍的場景,主要是圖片大小限制,場景復雜后,生成的gif圖片太大.

  這個功能當時我主界面完成后,就准備做的,但是當時一時想不到好的方案,如果針對每個Ogre屬性來生成UI,然后關聯每個對象,再想想后續如果要修改其中的字段屬性或是位置,這個工作量讓我不得不先想一個方案能夠避免這些問題.在完成第二篇天龍場景的加載時,順便在空閑時間想出現在的這種解決方法,不說這方法好,但是工作量少,簡潔,易修改,避免很多BUG.靈活性高,只有二個Ogre之間有關聯,就可以顯示在一起.如上面的Entity,他顯示出他父節點SceneNode,以及他的父類MovableObject的各屬性.

  這個方案簡單來說,分成三個部分,一個部分主要是XML文件,XML文件包含每個Ogre對象要關聯的對象,字段,關聯字段等.第二個部分是自動生成控件界面,根據第一部分的信息來生成各個界面.第三部分是根據Ogre對象更新各個控件,以及更新控件上的值更新后反饋給對應Ogre對象.如下是三個部分代碼圖,VS中C++代碼生成的代碼關聯圖中,聚合關系不能顯示出來,所以一些聚合信息在上面沒有顯示出來.

XML文件與基本類

  這個部分主要有二方面,一方面是XML文件保存的信息,包含每個Ogre對象關聯的對象,每個對象更新的字段,字段的項選擇,如下圖:

 

   然后就是如第一張圖中的類OgreStyle,我們可以看到,這個類三個部分都有引用到,也是我們的這解決方法的核心類,這個類是一個樹型結構,這個結構在這主要有二個好處,一是容易添加在樹型控件里.二是可以得到父節點信息與子節點信息.其中我們設計在刪除節點時,所有子節點全刪除.如下是OgreStyle代碼.

namespace Ogre3DX
{
    typedef std::vector<string> VectorString;
    class OgreStyle;
    typedef std::vector<OgreStyle*> OgreStyleVector;

    class OgreStyle
    {
    public:
        //ResourceType,SceneType
        int type = 0;
        std::string ogreName = "";
        std::string ogreSecond = "";
        std::string ogreText = "";

        OgreStyle* parent = nullptr;
        OgreStyleVector childs;        
    public:
        OgreStyle()
        {
            childs.clear();
        }

        std::string getText()
        {
            if (ogreText.empty())
                return ogreName;
            return ogreText;
        }

        void add(OgreStyle* child)
        {
            if (!check(child))
            {
                throw exception("child is parent.");
            }
            if (child->parent != nullptr)
            {
                auto index = find(child->parent->childs.begin(), child->parent->childs.end(), child);
                child->parent->childs.erase(index);
            }
            child->parent = this;
            childs.push_back(child);
        }

        void remove(OgreStyle* child)
        {
            remove(child, true);
        }

        void removeAll()
        {
            //刪除所有子節點
            while (!childs.empty())
            {
                auto child = childs.back();
                childs.pop_back();
                //這句 避免遞歸時重復delete
                child->parent = nullptr;
                if (child != nullptr)
                {
                    delete child;
                    child = nullptr;
                }
            }
        }

        OgreStyle* findChild(int type)
        {
            if (childs.empty())
                return nullptr;
            for (auto child : this->childs)
            {
                if (child->type == type)
                    return child;
            }
            return nullptr;
        }

        OgreStyle* findChild(std::string name)
        {
            if (childs.empty())
                return nullptr;
            for (auto child : this->childs)
            {
                if (child->ogreName == name)
                    return child;
            }
            return nullptr;
        }

        bool check(OgreStyle* style)
        {
            OgreStyle* parent = this->parent;
            while (parent != nullptr)
            {
                if (parent == style)
                    return false;
                parent = parent->parent;
            }
            return true;
        }

        ~OgreStyle()
        {
            if (parent != nullptr)
                parent->remove(this, false);
            
            //刪除所有子節點
            while (!childs.empty())
            {
                auto child = childs.back();
                childs.pop_back();
                //這句 避免遞歸時重復delete
                child->parent = nullptr;
                if (child != nullptr)
                {
                    delete child;
                    child = nullptr;
                }

            }
        }

    private:
        void remove(OgreStyle* child, bool bDelete)
        {
            if (child->parent != nullptr)
            {
                auto index = find(child->parent->childs.begin(), child->parent->childs.end(), child);
                child->parent->childs.erase(index);
            }
            child->parent = nullptr;
            if (bDelete)
            {
                delete child;
                child = nullptr;
            }
        }
    };
}
OgreStyle

  其中OgreStyle中的Type就是對應的Ogre中的對象類型,而ogreName是對應Ogre對象的標識,parent與childs樹結構.type對應的值在如下類中:

namespace ResourceEnum
{
    //對應Ogre資源類型
    enum ResourceType
    {
        Resource = 100,
        Material,
        Technique,
        Pass,
        Mesh,
        SubMesh,
        TextureUnit,
        Texture,
        Particle,
        Compositor,
        Program,
        Shader_Cg,
        Shader_hlsl,
        Shader_glsl,
    };

    //對應Ogre場景
    enum SceneType
    {
        Scene = 200,
        SceneNode,
        Viewport,
        Camera,
        RenderSystem,
        RenderTarget,
        MovableObject,
        Renderable
    };

    enum MovableType
    {
        MovableOther = 300,
        BillboardChain,
        BillboardSet,
        Entity,
        Light,
        ManualObject,
        ParticleSystem,
        RibbonTrail,
    };

    enum RenderType
    {
        RenderOther = 400,
        RenderSubEntity
    };

    enum FileType
    {
        FL_Materials = 500,
        FL_Material,
        FL_Group,
    };
}
ResourceType

  先暫時列這么多,其中上面第一份XML文件就是每個type關聯那些type,第二份文件就是每個type對應的字段,第三個type就是其中Ogre中的一些enum.下面這個類把type與三份三XML文件關聯起來.其中方法initXML不是有意要使用這種比較難理解的方式,主要是因為DataStreamHolder里面的數據是shared_ptr智能指針類型,最后得到的root只有在函數initXML內才有效,傳出這個值無效,而針對XML文件有效性檢查的代碼是一樣,這樣只有把方法傳過來.

namespace Ogre3DX
{
    struct NodeStyle
    {
        std::string nodeName;
        std::string nodeText;
        NodeStyle(){};
        NodeStyle(std::string name, std::string text)
            :nodeName(name), nodeText(text){}

        string getText()
        {
            if (nodeText.empty())
            {
                string result;
                for (auto ch = nodeName.begin(); ch != nodeName.end(); ++ch)
                {
                    if (ch != nodeName.begin() && isupper(*ch))
                    {
                        result.push_back(' ');
                    }
                    result.push_back(*ch);
                }
                return result;
            }
            return nodeText;
        }
    };

    struct OgreField
    {
        string fieldName;
        string fieldType;
        string fieldKey;
        string fieldText;
        bool fieldManual = false;
        OgreField(){};
        OgreField(std::string name, std::string type, std::string key)
            :fieldName(name), fieldType(type), fieldKey(key){}

        string getText()
        {
            if (fieldText.empty())
            {
                string result;
                for (auto ch = fieldName.begin(); ch != fieldName.end(); ++ch)
                {
                    if (ch != fieldName.begin() && isupper(*ch))
                    {
                        result.push_back(' ');
                    }
                    result.push_back(*ch);
                }
                return result;
            }
            return fieldText;
        }
    };

    struct RelationVisible
    {
        string name;
        string relationName;
        string relationValue;
        bool relationVisible;
        string nodeName;
        RelationVisible(std::string name, std::string dName, std::string value, bool visible, string node)
            :name(name), relationName(dName), relationValue(value), relationVisible(visible), nodeName(node){}
    };

    typedef std::vector<OgreField*> FieldVector;
    typedef std::vector<NodeStyle*> NodeStyleVector;
    typedef std::vector<RelationVisible*> RelationVector;
}

namespace Ogre3DX
{
    class OgreStyleManager :
        public tools::Singleton<OgreStyleManager>
    {
    public:
        OgreStyleManager();
        ~OgreStyleManager();

        void initialise();

        int getOgreStyleType(const string& name, int deault = 0)
        {
            for (auto kv : ogreStyles)
            {
                if (kv.second == name)
                    return kv.first;
            }
            return deault;
        }

        std::string getOgreStyleName(int type, const string& name = "")
        {
            if (ogreStyles.find(type) != ogreStyles.end())
            {
                return ogreStyles[type];
            }
            return name;
        }

        NodeStyleVector getNodeStyles(int type)
        {
            NodeStyleVector result;
            if (ogreStyles.find(type) != ogreStyles.end())
            {
                auto styleName = ogreStyles[type];
                if (ogreNodes.find(styleName) != ogreNodes.end())
                {
                    auto nodeNames = ogreNodes[styleName];
                    int size = nodeNames.size();
                    for (int i = 0; i < size; i++)
                    {
                        auto name = nodeNames[i];
                        auto nodeStyle = nodeStyles[name];
                        auto text = ogreTexts[styleName][i];
                        if (!text.empty())
                            nodeStyle->nodeText = text;
                        result.push_back(nodeStyle);
                    }
                }
            }
            return result;
        }

        FieldVector getFieldVector(const string& nodeName)
        {
            if (nodeFields.find(nodeName) != nodeFields.end())
            {
                return nodeFields[nodeName];
            }
            return FieldVector();
        }

        VectorString getKeyItems(const string& key)
        {
            if (keyItems.find(key) != keyItems.end())
            {
                return keyItems[key];
            }
            return VectorString();
        }

        RelationVector getRelationFields(const string& nodeName)
        {
            RelationVector result;
            for (auto rf : relationFields)
            {
                if (rf->nodeName == nodeName)
                    result.push_back(rf);
            }
            return result;
        }

        bool getRelation(const string& fieldName)
        {
            for (auto rf : relationFields)
            {
                if (rf->name == fieldName || rf->relationName == fieldName)
                    return true;
            }
            return false;
        }
    private:
        typedef void(OgreStyleManager::*ptrFun)(xml::ElementPtr root);
        void initXML(std::string xmlName, ptrFun func);

        void initOgreStyles();
        void initStyleNodes(xml::ElementPtr root);
        void initNodeFields(xml::ElementPtr root);
        void initKeyItems(xml::ElementPtr root);
        void initRelationFields();
        xml::ElementPtr getRoot(DataStreamHolder data);
    private:
        // OgreResource int -> OgreStyleName
        std::map<int, string> ogreStyles;
        //NodeName -> NodeStyle
        std::map<string, NodeStyle*> nodeStyles;
        //OgreStyleName -> Vector NodeName (StyleNode.xml)
        std::map<string, VectorString> ogreNodes;
        std::map<string, VectorString> ogreTexts;
        //NodeName -> Vector NodeField (NodeField.xml)
        std::map<string, FieldVector> nodeFields;
        //key - items (KeyItem.xml)
        std::map<string, VectorString> keyItems;

        RelationVector relationFields;
    };
}

template <> OgreStyleManager* tools::Singleton<OgreStyleManager>::msInstance = nullptr;

namespace Ogre3DX
{
    OgreStyleManager::OgreStyleManager()
    {

    }

    OgreStyleManager::~OgreStyleManager()
    {

    }

    void OgreStyleManager::initialise()
    {
        initOgreStyles();
        initXML("StyleNode.xml", &OgreStyleManager::initStyleNodes);
        initXML("NodeField.xml", &OgreStyleManager::initNodeFields);
        initXML("KeyItem.xml", &OgreStyleManager::initKeyItems);
        initRelationFields();
    }

    void OgreStyleManager::initOgreStyles()
    {
        ogreStyles[ResourceEnum::ResourceType::Resource] = "Resource";
        ogreStyles[ResourceEnum::ResourceType::Material] = "Material";
        ogreStyles[ResourceEnum::ResourceType::Technique] = "Technique";
        ogreStyles[ResourceEnum::ResourceType::Mesh] = "Mesh";
        ogreStyles[ResourceEnum::ResourceType::Texture] = "Texture";
        ogreStyles[ResourceEnum::ResourceType::Particle] = "Particle";
        ogreStyles[ResourceEnum::ResourceType::Compositor] = "Compositor";
        ogreStyles[ResourceEnum::ResourceType::Program] = "Program";
        ogreStyles[ResourceEnum::ResourceType::SubMesh] = "SubMesh";
        ogreStyles[ResourceEnum::ResourceType::Pass] = "Pass";

        ogreStyles[ResourceEnum::SceneType::Scene] = "SceneManager";
        ogreStyles[ResourceEnum::SceneType::SceneNode] = "SceneNode";
        ogreStyles[ResourceEnum::SceneType::Viewport] = "Viewport";
        ogreStyles[ResourceEnum::SceneType::Camera] = "Camera";
        ogreStyles[ResourceEnum::SceneType::RenderSystem] = "RenderSystem";
        ogreStyles[ResourceEnum::SceneType::RenderTarget] = "RenderTarget";
        ogreStyles[ResourceEnum::SceneType::MovableObject] = "MovableObject";
        ogreStyles[ResourceEnum::SceneType::Renderable] = "Renderable";

        ogreStyles[ResourceEnum::MovableType::MovableOther] = "MovableOther";
        ogreStyles[ResourceEnum::MovableType::BillboardChain] = "BillboardChain";
        ogreStyles[ResourceEnum::MovableType::BillboardSet] = "BillboardSet";
        ogreStyles[ResourceEnum::MovableType::Entity] = "Entity";
        ogreStyles[ResourceEnum::MovableType::Light] = "Light";
        ogreStyles[ResourceEnum::MovableType::ManualObject] = "ManualObject";
        ogreStyles[ResourceEnum::MovableType::ParticleSystem] = "ParticleSystem";
        ogreStyles[ResourceEnum::MovableType::RibbonTrail] = "RibbonTrail";

        ogreStyles[ResourceEnum::RenderType::RenderSubEntity] = "SubEntity";
    }

    void OgreStyleManager::initXML(std::string xmlName, ptrFun initFunc)
    {
        DataStreamHolder data = MyGUI::DataManager::getInstance().getData(xmlName);
        if (data.getData() == nullptr)
        {
            throw exception("");
        }

        xml::Document doc;
        if (!doc.open(data.getData()))
        {
            throw exception("");
        }

        xml::ElementPtr root = doc.getRoot();
        if ((nullptr == root) || (root->getName() != "OgreResource"))
        {
            throw exception("");
        }
        (this->*initFunc)(root);
    }

    void OgreStyleManager::initStyleNodes(xml::ElementPtr root)
    {
        auto node = root->getElementEnumerator();
        while (node.next("OgreStyle"))
        {
            auto name = node->findAttribute("name");
            auto field = node->getElementEnumerator();
            while (field.next())
            {
                auto nodeName = field->findAttribute("name");
                auto text = field->findAttribute("text");
                ogreNodes[name].push_back(nodeName);
                ogreTexts[name].push_back(text);
            }
        }
    }

    void OgreStyleManager::initNodeFields(xml::ElementPtr root)
    {
        auto node = root->getElementEnumerator();
        while (node.next("OgreNode"))
        {
            auto name = node->findAttribute("name");
            auto text = node->findAttribute("text");
            NodeStyle* nodeStyle = new NodeStyle(name, text);
            nodeStyles[name] = nodeStyle;
            auto field = node->getElementEnumerator();
            while (field.next())
            {
                auto fieldName = field->findAttribute("name");
                auto fieldType = field->findAttribute("type");
                auto fieldKey = field->findAttribute("key");
                auto fieldText = field->findAttribute("text");
                auto fieldManual = field->findAttribute("manual");
                OgreField* field = new OgreField(fieldName, fieldType, fieldKey);
                field->fieldText = fieldText;
                if (fieldManual.size() > 0)
                {
                    field->fieldManual = Ogre::StringConverter::parseBool(fieldManual);
                }
                nodeFields[name].push_back(field);
            }
        }
    }

    void OgreStyleManager::initKeyItems(xml::ElementPtr root)
    {
        auto node = root->getElementEnumerator();
        while (node.next("Key"))
        {
            auto name = node->findAttribute("name");
            auto field = node->getElementEnumerator();
            while (field.next())
            {
                auto nodeName = field->findAttribute("name");
                keyItems[name].push_back(nodeName);
            }
        }
    }

    void OgreStyleManager::initRelationFields()
    {
        
        RelationVisible* entitySkeleton = new RelationVisible("DisplaySkeleton", "HaveSkeleton", "false", false, "Entity");
        RelationVisible* targetPrimary0 = new RelationVisible("Update", "Primary", "true", false, "RenderTarget");
        RelationVisible* targetPrimary1 = new RelationVisible("Active", "Primary", "true", false, "RenderTarget");
        RelationVisible* targetPrimary2 = new RelationVisible("AutoUpdated", "Primary", "true", false, "RenderTarget");

        RelationVisible* spot = new RelationVisible("Spotlight", "Type", "SPOTLIGHT", true, "Light");
        RelationVisible* spotInner = new RelationVisible("SpotlightInner", "Type", "SPOTLIGHT", true, "Light");
        RelationVisible* spotOuter = new RelationVisible("SpotlightOuter", "Type", "SPOTLIGHT", true, "Light");
        RelationVisible* spotFalloff = new RelationVisible("SpotlightFalloff", "Type", "SPOTLIGHT", true, "Light");
        
        relationFields.push_back(entitySkeleton);
        relationFields.push_back(targetPrimary0);
        relationFields.push_back(targetPrimary1);
        relationFields.push_back(targetPrimary2);

        relationFields.push_back(spot);
        relationFields.push_back(spotInner);
        relationFields.push_back(spotOuter);
        relationFields.push_back(spotFalloff);
    }

    xml::ElementPtr OgreStyleManager::getRoot(DataStreamHolder data)
    {
        //Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource(_name, mGroup, true);
        //MyGUI::OgreDataStream* data = new MyGUI::OgreDataStream(stream);
        // "OgreNodes.xml");
        if (data.getData() == nullptr)
        {
            throw exception("");
        }

        xml::Document doc;
        if (!doc.open(data.getData()))
        {
            throw exception("");
        }

        xml::ElementPtr root = doc.getRoot();
        if ((nullptr == root) || (root->getName() != "OgreResource"))
        {
            throw exception("");
        }
        return root;
    }
}
OgreStyleManager

  通過這個類,我們知道,一個OgreStyle對應多個NodeStyle,就如Entity分別顯示Entiy, SceneNode, MovableObject 這三個節點,每個節點NodeStyle又包含多個OgreField,就如SceneManager包含name,typename,ambientLight等字段.上面的RelationVisible主要是指二個字段有主從關系的那種,如上面是聚光燈才顯示一些聚光燈的信息,后面也會放入XML文件中.

  然后就是OgreQuery,這個類比較復雜,在這我們只列出一個方法,就是得到我們現在樹型控件上的數據.

    OgreStyle* OgreQuery::getTLRoot()
    {
        ogreRoot = shared_ptr<OgreStyle>(new OgreStyle());
        auto ogreSceneRoot = new OgreStyle();

        auto scene = Ogre::Root::getSingleton().getSceneManager(DSceneName);
        ogreSceneRoot->type = ResourceEnum::SceneType::Scene;
        ogreSceneRoot->ogreName = scene->getName();
        ogreRoot->add(ogreSceneRoot);

        auto factorys = Ogre::Root::getSingleton().getMovableObjectFactoryIterator();
        while (factorys.hasMoreElements())
        {
            auto currentFactory = factorys.getNext();
            OgreStyle* factory = new OgreStyle();
            factory->ogreName = currentFactory->getType();
            factory->type = ResourceEnum::SceneType::MovableObject;
            ogreSceneRoot->add(factory);

            auto movables = scene->getMovableObjectIterator(factory->ogreName);
            while (movables.hasMoreElements())
            {
                auto currentMovable = movables.getNext();
                OgreStyle* movable = new OgreStyle();
                movable->ogreName = currentMovable->getName();
                int type = OgreStyleManager::getInstance().getOgreStyleType(currentMovable->getMovableType(),
                    ResourceEnum::MovableType::MovableOther);
                movable->type = type;
                factory->add(movable);
                if (type == ResourceEnum::MovableType::Entity)
                {
                    Ogre::Entity* entity = dynamic_cast<Ogre::Entity*>(currentMovable);
                    if (MyGUI::utility::startWith(movable->ogreName, "Unnamed"))
                    {
                        movable->ogreText = entity->getMesh()->getName();
                    }
                    int subCount = entity->getNumSubEntities();
                    for (int i = 0; i < subCount; i++)
                    {
                        auto subEntity = entity->getSubEntity(i);
                        OgreStyle* subStyle = new OgreStyle();
                        movable->add(subStyle);
                        getRenderable(subEntity, subStyle, ResourceEnum::RenderType::RenderSubEntity, i);
                    }
                }
            }
        }
        auto ogreRenderRoot = new OgreStyle();
        ogreRoot->add(ogreRenderRoot);
        auto render = Ogre::Root::getSingleton().getRenderSystem();
        getRenderSystem(render, ogreRenderRoot);

        return ogreRoot.get();
    }
getTLRoot

  這個類現在設計了二種顯示方式,一種是上面的這種,不顯示SceneNode,還有一種是顯示SceneNode,這里就不放出來了,和上面這個方法差不多.

自動生成界面

  第二部分全是UI顯示部分,主要是控件顯示分類,控件創建,顯示,更新.

  我們把我們要的控件分成如下幾種顯示方式,大部分是從MyGUI中的LayoutEditor中修改后直接使用.

  1.PropertyFieldLabel:主要是顯示一個文本,主要用於表示分組.

  2.PropertyFieldEditBox:顯示一個文本與一個值,一般用來設定字符串,如Technique象的名稱.

  3.PropertyFieldColour:顏色選擇.對應對象的顏色.

  4.PropertyFieldCheckBox:對應Bool類型,是或否.

  5.PropertyFieldComboBox:對應多項選擇,如Ogre常見的Enum,對光源類型,對應NodeField.xml文件中的type="ComboBox",這種type一般后面會有一個key的屬性,用於去KeyItem.xml查找對應的多項顯示,如光源類型對應的點光源,方向光源,聚光燈這些.

  6.PropertyFieldAutoComplete:自定義MyGUI控件AutoComplete的包裝,用於項非常多的時候,如天龍中,一個場景中Mesh與Material,一般成百上千的,下拉框根本顯示不過來,AutoComplete能根據輸入給出最適合的選擇.后面會專門寫一篇針對MyGUI各控件的擴展.

  7.PropertyFieldNumeric:對應各type有n int,n float,用於檢查輸入是否合法.

  其中PropertyFieldManager用於管理這些類,我們先看這個類的實現.

namespace tools
{

    class PropertyFieldManager :
        public MyGUI::Singleton<PropertyFieldManager>
    {
    public:
        PropertyFieldManager();
        virtual ~PropertyFieldManager();

        void initialise();
        void shutdown();

        IPropertyField* createPropertyField(MyGUI::Widget* _window, const std::string& _type);

    private:
        typedef MyGUI::delegates::CDelegate2<IPropertyField*&, MyGUI::Widget*> Delegate;
        typedef std::map<std::string, Delegate> MapFactoryItem;
        MapFactoryItem mFactories;
    };
}
template <> tools::PropertyFieldManager* MyGUI::Singleton<tools::PropertyFieldManager>::msInstance = nullptr;
template <> const char* MyGUI::Singleton<tools::PropertyFieldManager>::mClassTypeName = "PropertyFieldManager";

namespace tools
{

    template <typename Type>
    class GenericFactory
    {
    public:
        typedef MyGUI::delegates::CDelegate2<IPropertyField*&, MyGUI::Widget*> Delegate;
        static typename Delegate::IDelegate* getFactory()
        {
            return MyGUI::newDelegate(createFromFactory);
        }

    private:
        static void createFromFactory(IPropertyField*& _instance, MyGUI::Widget* _parent)
        {
            _instance = new Type(_parent);
        }
    };

    PropertyFieldManager::PropertyFieldManager()
    {
    }

    PropertyFieldManager::~PropertyFieldManager()
    {
    }

    void PropertyFieldManager::initialise()
    {
        mFactories["EditBox"] = GenericFactory<PropertyFieldEditBox>::getFactory();
        mFactories["Label"] = GenericFactory<PropertyFieldLabel>::getFactory();
        mFactories["Colour"] = GenericFactory<PropertyFieldColour>::getFactory();
        mFactories["Bool"] = GenericFactory<PropertyFieldCheckBox>::getFactory();
        mFactories["ComboBox"] = GenericFactory<PropertyFieldComboBox>::getFactory();
        mFactories["Auto"] = GenericFactory<PropertyFieldAutoComplete>::getFactory();

        mFactories["StaticEditBox"] = GenericFactory<PropertyFieldStaticEditBox>::getFactory();
        mFactories["1 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
        mFactories["2 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
        mFactories["4 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
        mFactories["1 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
        mFactories["2 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
        mFactories["3 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
        mFactories["4 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
    }

    void PropertyFieldManager::shutdown()
    {
    }

    IPropertyField* PropertyFieldManager::createPropertyField(MyGUI::Widget* _window, const std::string& _type)
    {
        IPropertyField* result = nullptr;

        MapFactoryItem::iterator item = mFactories.find(_type);
        MYGUI_ASSERT(item != mFactories.end(), "Factory PropertyField '" << _type << "' not found.");

        (*item).second(result, _window);
        result->initialise(_type);

        return result;
    }

}
PropertyFieldManager

  PropertyFieldManager主要是根據不同的NodeField.xml中NodeField中的Type來生成對應IPropertyField.IPropertyField是上面各字段控件的基類,我們來看下他的實現,了解所有字段控件實現能那些功能.對比Mygui中的LayoutEditor中的IPropertyField有些改動主要一是為了讓IPropertyField承載更多公共實現,免的子類型的各部分代碼差不多.二是更好的適合我們自己的項目,針對Ogre的各屬性更改.

using namespace Ogre3DX;
namespace tools
{
    typedef MyGUI::delegates::CDelegate3<const std::string&, const std::string&, bool> PropertyFieldActionDelegate;

    class IPropertyField
    {
    public:
        virtual ~IPropertyField();

        virtual void initialise(const std::string& _type);

        virtual void setValue(const std::string& _value) = 0;
        //Relation Field
        virtual string getValue();
        virtual void onFillValues();

        virtual void setField(OgreField* _field);
        virtual OgreField* getField();
        //Update Field
        virtual bool getFocus();

        virtual void setName(const std::string& _value);
        virtual void setVisible(bool _value);
        virtual bool getVisible();

        virtual void onAction(const std::string& _value, bool _final);
        virtual bool onCheckValue();

        virtual MyGUI::IntSize getContentSize();
        virtual void setCoord(const MyGUI::IntCoord& _coord);
        virtual bool checkFocus(MyGUI::Widget* widget);

        PropertyFieldActionDelegate eventAction;
    protected:
        MyGUI::TextBox* mText = nullptr;
        std::string mType;
        std::string mName;

        OgreField* field = nullptr;

        MyGUI::Widget* mainWidget = nullptr;
    };
}
namespace tools
{
    IPropertyField::~IPropertyField()
    {
    }

    void IPropertyField::initialise(const std::string& _type)
    {
        mType = _type;
    }

    string IPropertyField::getValue()
    {
        return "";
    }

    void IPropertyField::onFillValues()
    {
    }

    void IPropertyField::setField(OgreField* _field)
    {
        field = _field;
        onFillValues();
    }

    OgreField* IPropertyField::getField()
    {
        return field;
    }

    bool IPropertyField::getFocus()
    {
        //InputManager::getInstance().getKeyFocusWidget();
        return false;
    }

    bool IPropertyField::checkFocus(MyGUI::Widget* widget)
    {
        auto mouseF = MyGUI::InputManager::getInstance().getMouseFocusWidget();
        if (mouseF == widget)
            return true;
        auto keyF = MyGUI::InputManager::getInstance().getKeyFocusWidget();
        if (keyF == widget)
            return true;
        return false;
    }

    void IPropertyField::onAction(const std::string& _value, bool _final)
    {
        eventAction(field->fieldName, _value, _final);
    }

    bool IPropertyField::onCheckValue()
    {
        return true;
    }

    MyGUI::IntSize IPropertyField::getContentSize()
    {
        return MyGUI::IntSize(0, mainWidget->getHeight());
    }

    void IPropertyField::setCoord(const MyGUI::IntCoord& _coord)
    {
        mainWidget->setCoord(_coord);
    }

    void IPropertyField::setName(const std::string& _value)
    {
        mName = _value;
        mText->setCaption(_value);
    }

    void IPropertyField::setVisible(bool _value)
    {
        mainWidget->setVisible(_value);
    }

    bool IPropertyField::getVisible()
    {
        return mainWidget->getVisible();
    }

}
IPropertyField

   可以看到IPropertyField與第一部分中的OgreField是一一對應的,拿一些方法說下.

  1.initialise():子類主要根據對應的Layout文件生成各個控件.在這主要把BaseLayout中的mMainWidget給IPropertyField中的mainWidget,這樣子類就不需要每個來實現getVisible,setVisible,getContentSize,setCoord就不需要子類型去實現了.

  2.setValue,getValue:設置和得到IPropertyField控件中的值.

  3.setField,getField:設置和得到對應的OgreField.

  4.getFocus:檢查當前IPropertyField是否在得到鼠標或鍵盤的焦點.如果是,我們不去更新上面的值.

  5.onAction:當我們更新IPropertyField中的值后,我們需要通知事件上的各個方法.

  如下是PropertyFieldComboBox的具體實現,這個類可以讓我們更好理解相關方法需要如何實現.

namespace tools
{
    class PropertyFieldComboBox :
        public wraps::BaseLayout,
        public IPropertyField
    {
    public:
        PropertyFieldComboBox(MyGUI::Widget* _parent);
        virtual ~PropertyFieldComboBox();

        virtual void setValue(const std::string& _value);
        virtual string getValue();
    protected:
        virtual void onFillValues();

    private:
        void notifyApplyProperties(MyGUI::Widget* _sender);
        void notifyForceApplyProperties2(MyGUI::ComboBox* _widget, size_t _index);

    protected:
        MyGUI::ComboBox* mField;

    };
}
namespace tools
{
    PropertyFieldComboBox::PropertyFieldComboBox(MyGUI::Widget* _parent) :
        BaseLayout("PropertyFieldComboBox.layout", _parent),
        mField(nullptr)
    {
        assignWidget(mText, "Text");
        assignWidget(mField, "Field");
        mainWidget = mMainWidget;

        mField->eventComboAccept += newDelegate(this, &PropertyFieldComboBox::notifyForceApplyProperties2);
    }

    PropertyFieldComboBox::~PropertyFieldComboBox()
    {
    }

    void PropertyFieldComboBox::onFillValues()
    {
        if (field == nullptr || field->fieldKey.empty())
            return;
        mField->removeAllItems();
        auto items = OgreStyleManager::getInstance().getKeyItems(field->fieldKey);
        for (auto item : items)
        {
            mField->addItem(item);
        }
    }

    void PropertyFieldComboBox::notifyApplyProperties(MyGUI::Widget* _sender)
    {
        std::string value = MyGUI::utility::toString(mField->getIndexSelected());// mField->getOnlyText();
        onAction(value, true);
    }

    void PropertyFieldComboBox::notifyForceApplyProperties2(MyGUI::ComboBox* _sender, size_t _index)
    {
        notifyApplyProperties(_sender);
    }

    void PropertyFieldComboBox::setValue(const std::string& _value)
    {
        int index = MyGUI::utility::parseInt(_value);
        if (index >= 0 && index < mField->getItemCount())
            mField->setItemSelect(index);
        //mField->setOnlyText(_value);
    }

    string PropertyFieldComboBox::getValue()
    {
        return mField->getOnlyText();
    }
}
PropertyFieldComboBox

   當我們知道如何根據OgreField生成IPropertyField后,如前面一個OgreStyle對應多個NodeStyle,每個節點NodeStyle又包含多個OgreField.對應PropertiesPanelView包含多個PanelProperties.每個PanelProperties又包含多個IPropertyField.

  當PropertiesPanelView得到OgreStyle后,根據OgreStyle的NodeStyle生成PanelProperties,然后PanelProperties根據對應的NodeStyle中的OgreField 生成IPropertyField.這樣OgreStyle需要的各個控件顯示出來了,如下是相關PropertiesPanelView與PanelProperties代碼.

namespace tools
{
    class PropertiesPanelView :
        public wraps::BaseLayout
    {
    public:
        PropertiesPanelView(MyGUI::Widget* _parent = nullptr);
        virtual ~PropertiesPanelView();
        void notifyChangeSelectedWidget(OgreStyle* data);
        void hideAllPanel();
    private:
        void notifyWindowChangeCoord(MyGUI::Window* _sender);

        std::vector<PanelProperties*> getPropertyWindow(OgreStyle* _style);
        size_t getIndexPanel(PanelProperties* _panel);

        void onRefleshFiled(float time);
    private:
        MyGUI::IntSize mOldSize;
        PanelView* mPanelView;

        typedef std::map<string, PanelProperties*> MapPropertyWindow;
        MapPropertyWindow mMapPropertyWindow;

        OgreStyle* currentStyle;
    };
}
namespace tools
{

    PropertiesPanelView::PropertiesPanelView(MyGUI::Widget* _parent) :
        BaseLayout("PropertiesPanelView.layout", _parent),
        mPanelView(nullptr),
        currentStyle(nullptr)
    {
        assignBase(mPanelView, "scroll_View");

        MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>(false);
        if (window != nullptr)
        {
            window->eventWindowChangeCoord += MyGUI::newDelegate(this, &PropertiesPanelView::notifyWindowChangeCoord);
            mOldSize = window->getSize();
        }
        notifyChangeSelectedWidget(nullptr);

        Gui::getInstance().eventFrameStart += MyGUI::newDelegate(this, &PropertiesPanelView::onRefleshFiled);
    }

    PropertiesPanelView::~PropertiesPanelView()
    {
        mPanelView->removeAllItems();

        for (MapPropertyWindow::iterator item = mMapPropertyWindow.begin(); item != mMapPropertyWindow.end(); ++item)
            delete (*item).second;
        mMapPropertyWindow.clear();
    }

    void PropertiesPanelView::notifyWindowChangeCoord(MyGUI::Window* _sender)
    {
        const MyGUI::IntSize& size = _sender->getSize();
        if (size != mOldSize)
        {
            mOldSize = size;
            mPanelView->setNeedUpdate();
        }
    }

    void PropertiesPanelView::notifyChangeSelectedWidget(OgreStyle* _currentStyle)
    {
        currentStyle = _currentStyle;

        for (MapPropertyWindow::iterator item = mMapPropertyWindow.begin(); item != mMapPropertyWindow.end(); ++item)
        {
            (*item).second->setVisible(false);
            (*item).second->setStyle(nullptr, nullptr);
            (*item).second->update();
        }
        if (currentStyle == nullptr)
            return;
        auto panels = getPropertyWindow(currentStyle);
        for (auto panel : panels)
        {
            panel->setVisible(true);
            panel->update();
        }
    }

    void PropertiesPanelView::hideAllPanel()
    {
        for (MapPropertyWindow::iterator item = mMapPropertyWindow.begin(); item != mMapPropertyWindow.end(); ++item)
        {
            (*item).second->setVisible(false);
            (*item).second->setStyle(nullptr, nullptr);
            (*item).second->update();
        }
    }

    std::vector<PanelProperties*> PropertiesPanelView::getPropertyWindow(OgreStyle* _style)
    {
        std::vector<PanelProperties*> result;
        auto nodeStyles = OgreStyleManager::getInstance().getNodeStyles(_style->type);
        for (auto nodeStyle : nodeStyles)
        {
            if (nodeStyle == nullptr || nodeStyle->nodeName.empty())
                continue;
            std::string key = MyGUI::utility::toString(_style->type) + nodeStyle->nodeName;
            MapPropertyWindow::iterator item = mMapPropertyWindow.find(key);
            if (item == mMapPropertyWindow.end())
            {
                PanelProperties* panel = new PanelProperties();
                mPanelView->addItem(panel);// insertItem(getIndexByDepth(_depth), result);
                mMapPropertyWindow[key] = panel;
            }
            mMapPropertyWindow[key]->setStyle(_style, nodeStyle);
            result.push_back(mMapPropertyWindow[key]);
        }
        return result;
    }

    size_t PropertiesPanelView::getIndexPanel(PanelProperties* _panel)
    {
        for (size_t index = 0; index < mPanelView->getItemCount(); ++index)
        {
            if (mPanelView->getItem(index) == _panel)
                return index;
        }
        return MyGUI::ITEM_NONE;
    }

    void PropertiesPanelView::onRefleshFiled(float time)
    {
        for (auto item = mMapPropertyWindow.begin(); item != mMapPropertyWindow.end(); ++item)
        {
            if ((*item).second->getVisible())
            {
                (*item).second->onReflesh();
            }
        }
    }
}
PropertiesPanelView
using namespace Ogre3DX;
namespace tools
{
    typedef std::vector<std::pair<std::string, IPropertyField*>> MapPropertyField;
    typedef MapPropertyField::iterator FieldIter;

    class PanelProperties :
        public wraps::BasePanelViewItem
    {
    public:

        PanelProperties();

        virtual void initialise();
        virtual void shutdown();

        void setStyle(OgreStyle* ogreStyle, NodeStyle* nodeStyle);
        void update();

        void onReflesh();
    private:
        void notifyAction(const std::string& _name, const std::string& _value, bool _final);

        size_t addParametrs();
        void destroyPropertyFields();
        void hidePropertyFields();
        void updateSize();
        void updateRelationFields();

        IPropertyField* getPropertyField(MyGUI::Widget* _client, OgreField* field);
        FieldIter getField(const std::string& name);
    private:
        //typedef std::map<std::string, IPropertyField*> MapPropertyField;
        MapPropertyField mFields;
        OgreStyle* currentOgreStyle = nullptr;
        NodeStyle* currentNodeStyle = nullptr;
    };

}
namespace tools
{

    PanelProperties::PanelProperties() :
        BasePanelViewItem("PanelProperties.layout")
    {
    }

    void PanelProperties::initialise()
    {
    }

    void PanelProperties::shutdown()
    {
        destroyPropertyFields();
    }

    size_t PanelProperties::addParametrs()
    {
        size_t result = 0;
        if (currentNodeStyle != nullptr)
        {
            auto fields = OgreStyleManager::getInstance().getFieldVector(currentNodeStyle->nodeName);
            for (auto iter = fields.begin(); iter != fields.end(); ++iter)
            {
                IPropertyField* field = getPropertyField(mWidgetClient, (*iter));
                field->setField(*iter);
                field->setValue("");
                result++;
            }
        }
        return result;
    }

    void PanelProperties::setStyle(OgreStyle* ogreStyle, NodeStyle* nodeStyle)
    {
        currentOgreStyle = ogreStyle;
        currentNodeStyle = nodeStyle;
    }

    void PanelProperties::update()
    {
        hidePropertyFields();

        if (currentNodeStyle != nullptr)
            mPanelCell->setCaption(currentNodeStyle->getText());

        size_t count = addParametrs();

        setVisible(count > 0);
        onReflesh();
        updateSize();
        updateRelationFields();
    }

    void PanelProperties::onReflesh()
    {
        for (MapPropertyField::iterator item = mFields.begin(); item != mFields.end(); ++item)
        {
            auto fieldLayout = (*item).second;
            if (fieldLayout->getVisible() && !fieldLayout->getFocus())
            {
                auto field = fieldLayout->getField();
                if (field->fieldManual)
                    continue;
                if (currentOgreStyle != nullptr && currentNodeStyle != nullptr && field != nullptr)
                {
                    int type = OgreStyleManager::getInstance().getOgreStyleType(currentNodeStyle->nodeName);
                    if (type != 0)
                    {
                        auto text = OgreManager::getInstance().getValue(currentOgreStyle, type, field->fieldName);
                        fieldLayout->setValue(text);
                    }
                }
            }
        }
    }

    void PanelProperties::updateSize()
    {
        int height = 0;

        for (MapPropertyField::iterator item = mFields.begin(); item != mFields.end(); ++item)
        {
            if ((*item).second->getVisible())
            {
                MyGUI::IntSize size = (*item).second->getContentSize();
                (*item).second->setCoord(MyGUI::IntCoord(0, height, mMainWidget->getWidth(), size.height));
                height += size.height;
            }
        }
        mPanelCell->setClientHeight(height);
    }

    void PanelProperties::updateRelationFields()
    {
        if (currentNodeStyle == nullptr)
            return;
        RelationVector rfs = OgreStyleManager::getInstance().getRelationFields(currentNodeStyle->nodeName);
        if (rfs.size() == 0)
            return;
        bool change = false;

        for (auto rf : rfs)
        {
            auto source = getField(rf->name);
            auto dest = getField(rf->relationName);
            if (source != mFields.end() && dest != mFields.end())
            {
                auto nowValue = (*dest).second->getValue();
                bool nowVisable = (*source).second->getVisible();
                if ((nowValue == rf->relationValue) != (nowVisable == rf->relationVisible))
                {
                    change = true;
                    (*source).second->setVisible(!nowVisable);
                }
            }
        }
        if (change)
        {
            updateSize();
        }
    }

    void PanelProperties::destroyPropertyFields()
    {
        for (MapPropertyField::iterator item = mFields.begin(); item != mFields.end(); ++item)
            delete (*item).second;
        mFields.clear();
    }

    void PanelProperties::notifyAction(const std::string& _name, const std::string& _value, bool _final)
    {
        //if (currentOgreStyle != nullptr)
        //{
        //    currentOgreStyle->setValue(_name, _value);
        //}
        if (currentOgreStyle != nullptr && currentNodeStyle != nullptr)
        {
            int type = OgreStyleManager::getInstance().getOgreStyleType(currentNodeStyle->nodeName);
            if (type != 0)
            {
                OgreManager::getInstance().setValue(currentOgreStyle, type, _name, _value);
            }
        }
        updateRelationFields();
    }

    void PanelProperties::hidePropertyFields()
    {
        for (MapPropertyField::iterator item = mFields.begin(); item != mFields.end(); ++item)
            (*item).second->setVisible(false);
    }

    FieldIter PanelProperties::getField(const std::string& name)
    {
        for (auto i = mFields.begin(); i != mFields.end(); ++i)
        {
            if ((*i).first == name)
                return i;
        }
        return mFields.end();
    }

    IPropertyField* PanelProperties::getPropertyField(MyGUI::Widget* _client, OgreField* field)
    {
        MapPropertyField::iterator item = getField(field->fieldName);
        if (item != mFields.end())
        {
            (*item).second->setVisible(true);
            return (*item).second;
        }

        IPropertyField* result = PropertyFieldManager::getInstance().createPropertyField(_client, field->fieldType);
        result->setName(field->getText());
        result->eventAction = MyGUI::newDelegate(this, &PanelProperties::notifyAction);

        //mFields[_name] = result;
        mFields.push_back(std::make_pair(field->fieldName, result));
        return result;
    }
}
PanelProperties

  這樣第二部分就把XML與控件生成關聯起來了,下面是第三部分,主要管理控件更新.

Oger對象驅動更新界面

  最開始我這邊的處理是OgreStyle里關聯一個Ogre對象,如OgreStyle中的type是Camera,就關聯一個Ogre中的Camera對象,這樣相應更新就直接根據這個對象來處理,不過后面沒有這樣處理,主要是如下考慮,一是OgreStyle被太多類引用,最好不要過於復雜,首先更新必然會修改到更多類,其次在C++中更新這種很多類引用的文件編譯都要等死個人.二是我們的需求,如前面所說Entity類型的OgreStyle需要更新Entity的屬性,也可能是MovableObject或SceneNode,這樣綁定OgreStyle與Ogre對象后,並不好處理這種情況,因為並不是一對一,根據修改的Field的名字來查找對應的NodeStyle並不是一個好的選擇.基於上面考慮,放棄這種想法.

  我們仔細想一下,應該是每一種type一種更新方式,並不和OgreStyle有關,而是和OgreStyle中的type有關,如OgreStyle為Entity,但是type可能是Entiyt, MovableObject, SceneNode這三種,我們可以根據type查找如何更新Ogre對象各字段的方法,然后根據OgreStyle的Ogrename找到對象.如下是代碼:

namespace Ogre3DX
{
    class OgreManager :
        public tools::Singleton<OgreManager>
    {
    public:
        OgreManager()
        {
            mapObject[ResourceEnum::ResourceType::Resource] = new OgreResource();
            mapObject[ResourceEnum::ResourceType::Material] = new OgreMaterial();
            mapObject[ResourceEnum::ResourceType::Technique] = new OgreTechnique();
            mapObject[ResourceEnum::ResourceType::Pass] = new OgrePass();
            mapObject[ResourceEnum::SceneType::Viewport] = new OgreViewport();

            mapObject[ResourceEnum::SceneType::SceneNode] = new OgreSceneNode();
            mapObject[ResourceEnum::SceneType::MovableObject] = new OgreMovableObject();
            mapObject[ResourceEnum::SceneType::RenderTarget] = new OgreRenderTarget();
            mapObject[ResourceEnum::SceneType::Camera] = new OgreCamera();
            mapObject[ResourceEnum::SceneType::Renderable] = new OgreRenderable();
            mapObject[ResourceEnum::SceneType::Scene] = new OgreSceneManager();

            mapObject[ResourceEnum::MovableType::Entity] = new OgreEntity();
            mapObject[ResourceEnum::MovableType::Light] = new OgreLight();
            scene = Ogre::Root::getSingleton().getSceneManager(DSceneName);
        }

        std::string OgreManager::getValue(OgreStyle* style, int type, const string& field)
        {
            OgreBasic* basic = getObject(style, type);
            if (basic != nullptr && basic->getObject() != nullptr)
            {
                return basic->getValue(field);
            }
            return "";
        }

        bool OgreManager::setValue(OgreStyle* style, int type, const string& field, const string& value)
        {
            OgreBasic* basic = getObject(style, type);
            if (basic != nullptr && basic->getObject() != nullptr)
            {
                return basic->setValue(field, value);
            }
            return false;
        }


        OgreBasic* getObject(OgreStyle* style, int type)
        {
            auto object = getType(type);
            if (object == nullptr)
                return nullptr;
            //重新設置ogre值.
            if (style != preStyle || type != preType)
            {
                fillStyleObject(style, type);
                preStyle = style;
                preType = type;
            }
            return object;
        }

        OgreBasic* getType(int type)
        {
            if (mapObject.find(type) != mapObject.end())
            {
                return mapObject[type];
            }
            return nullptr;
        }


        void fillStyleObject(OgreStyle* style, int type)
        {
            auto object = getType(type);
            if (object == nullptr)
                return;

            switch (style->type)
            {
            case ResourceEnum::SceneType::Scene:
                if (true)
                {
                    if (type == ResourceEnum::SceneType::Scene)
                    {
                        object->setObject(scene);
                    }
                    else if (type == ResourceEnum::SceneType::Viewport)
                    {
                        object->setObject(scene->getCurrentViewport());
                    }
                }
                break;
            case ResourceEnum::MovableType::Entity:
                if (true)
                {
                    auto entity = scene->getEntity(style->ogreName);
                    if (type == ResourceEnum::MovableType::Entity)
                    {
                        object->setObject(entity);
                    }
                    fillMovableable(object, entity, type);
                }
                break;
            case ResourceEnum::ResourceType::Material:
                if (true)
                {
                    auto mat = MaterialManager::getSingleton().getByName(style->ogreName);
                    fillMaterial(object, mat, type);
                }
                break;
            case ResourceEnum::RenderType::RenderSubEntity:
                if (style->parent != nullptr && style->parent->type == ResourceEnum::MovableType::Entity)
                {
                    auto entity = scene->getEntity(style->parent->ogreName);
                    int index = Ogre::StringConverter::parseInt(style->ogreName);
                    auto subEntity = entity->getSubEntity(index);
                    fillRenderable(object, subEntity, type);
                }
                break;
            case ResourceEnum::SceneType::RenderTarget:
                if (true)
                {
                    auto render = Ogre::Root::getSingleton().getRenderSystem();
                    auto target = render->getRenderTarget(style->ogreName);
                    if (type == ResourceEnum::SceneType::RenderTarget)
                    {
                        object->setObject(target);
                    }
                }
                break;
            case ResourceEnum::SceneType::Viewport:
                if (true)
                {
                    RenderSystem* render = Ogre::Root::getSingleton().getRenderSystem();
                    if (style->parent != nullptr && style->parent->type == ResourceEnum::SceneType::RenderTarget)
                    {
                        auto target = render->getRenderTarget(style->parent->ogreName);
                        int index = Ogre::StringConverter::parseInt(style->ogreName);
                        auto viewport = target->getViewport(index);
                        auto camera = viewport->getCamera();
                        if (type == ResourceEnum::SceneType::Viewport)
                        {
                            object->setObject(viewport);
                        }
                        else if (camera != nullptr)
                        {
                            fillCamera(object, camera, type);
                        }
                    }
                }
                break;
            case ResourceEnum::SceneType::Camera:
                if (true)
                {
                    auto camera = scene->getCamera(style->ogreName);
                    if (camera != nullptr)
                    {
                        fillCamera(object, camera, type);
                    }
                }
                break;
            case ResourceEnum::MovableType::Light:
                if (true)
                {
                    auto light = scene->getLight(style->ogreName);
                    if (type == ResourceEnum::MovableType::Light)
                    {
                        object->setObject(light);
                    }
                    fillMovableable(object, light, type);
                }
                break;
            default:
                break;
            }
        }
        void fillCamera(OgreBasic* object, Camera* camera, int type)
        {
            if (type == ResourceEnum::SceneType::Camera)
            {
                object->setObject(camera);
            }
            else if (type == ResourceEnum::SceneType::MovableObject)
            {
                object->setObject(camera);
            }
            else if (type == ResourceEnum::SceneType::Renderable)
            {
                object->setObject(camera);
            }
        }

        void fillRenderable(OgreBasic* object, Renderable* render, int type)
        {
            if (type == ResourceEnum::SceneType::Renderable)
            {
                object->setObject(render);
            }
            else if (type == ResourceEnum::ResourceType::Material)
            {
                object->setObject(render->getMaterial().get());
            }
            else if (type == ResourceEnum::ResourceType::Technique)
            {
                auto tech = render->getTechnique();
                object->setObject(tech);
            }
            else if (type == ResourceEnum::ResourceType::Pass)
            {
                auto pass = render->getTechnique()->getPass(0);
                object->setObject(pass);
            }
        }

        void fillMaterial(OgreBasic* object, MaterialPtr material, int type)
        {
            if (type == ResourceEnum::ResourceType::Material)
            {
                object->setObject(material.get());
            }
            else if (type == ResourceEnum::ResourceType::Resource)
            {
                object->setObject(material.get());
            }
            else if (type == ResourceEnum::ResourceType::Technique)
            {
                auto tech = material->getBestTechnique();
                if (tech == nullptr)
                {
                    tech = material->getTechnique(0);
                }
                object->setObject(tech);
            }
            else if (type == ResourceEnum::ResourceType::Pass)
            {
                auto tech = material->getBestTechnique();
                if (tech == nullptr)
                {
                    tech = material->getTechnique(0);
                }
                object->setObject(tech->getPass(0));
            }
        }

        void fillMovableable(OgreBasic* object, MovableObject* movable, int type)
        {
            if (type == ResourceEnum::SceneType::MovableObject)
            {
                object->setObject(movable);
            }
            else if (type == ResourceEnum::SceneType::SceneNode)
            {
                object->setObject(movable->getParentSceneNode());
            }
        }

        ~OgreManager()
        {

        }

    private:
        std::map<int, OgreBasic*> mapObject;
        //OgreBasic* pre
        OgreStyle* preStyle = nullptr;
        int preType = 0;
        SceneManager* scene = nullptr;
    };
}
OgreManager

  其中OgreBasic是所有ogre對象更新類的基類,OgreTemplate是一個泛型類,提供一些方便.如下代碼:

using namespace Ogre;

typedef Ogre::StringConverter format;
namespace Ogre3DX
{
    class OgreBasic
    {
    public:
        virtual std::string getValue(std::string field) = 0;
        virtual bool setValue(std::string field, std::string value) = 0;
        virtual void setObject(void* object){};
        virtual void* getObject(){ return nullptr; }
    };

    template<typename T>
    class OgreTemplate
        : public OgreBasic
    {
    public:
        OgreTemplate()
        {
        }

        virtual ~OgreTemplate(){};

        virtual std::string getValue(std::string field)
        {
            return "";
        }

        virtual bool setValue(std::string field, std::string value)
        {
            return false;
        }

        virtual void setObject(T* object)
        {
            ogreObject = object;
        }

        virtual T* getOgreObject()
        {
            return ogreObject;
        }
    protected:
        virtual void setObject(void* object)
        {
            return setObject((T*)object);
        }
        virtual void* getObject()
        {
            if (ogreObject == nullptr)
                return nullptr;
            return (void*)ogreObject;
        }

    protected:
        T* ogreObject = nullptr;
    };
}
OgreBasic OgreTemplate

  其中OgreBasic是根據字段名得到對應Ogre屬性的值,setValue是對某個字段賦值,setObject是傳入相關Ogre對象指針,getObject是返回某對象指針,而OgreTemplate主要給我們提供一些方便,如上面都是void*指針,在C#中相當於object一樣,需要強制轉化后才能用,這個OgreTemplate自動幫我們做這些事件,基於最少可見原則,在OgreTemplate我們把父類中的setObject與getObject隱藏,我們並不希望用OgreTemplate泛型類后還在使用對應void*指針,這樣可以避免很多不安全的問題.而OgreBasic本身是給OgreManager使用,在這個類中,會保證生成正確的OgreTemplate,傳入正確的Ogre對象指針.

  在這我們給出OgreSceneManager的一個OgreTemplate泛型具體化實現.

namespace Ogre3DX
{
    class OgreSceneManager :
        public OgreTemplate<Ogre::SceneManager>
    {
    private:
        Plane* plane = nullptr;
        std::string planeMaterial = "";
        FogMode fogMode = FogMode::FOG_NONE;
        ColourValue fogColor = ColourValue::White;
        Real fogDensity = 0.001;
        Real fogStart = 0.0;
        Real fogEnd = 1.0;
    public:
        OgreSceneManager()
        {
            plane = new Plane(0, -1, 0, 5000);
        }
        ~OgreSceneManager()
        {
            if (plane != nullptr)
            {
                delete plane;
                plane = nullptr;
            }
        }

        virtual std::string getValue(std::string field)
        {
            std::string result = "";
            if (field == "Name")
            {
                result = ogreObject->getName();
            }
            else if (field == "TypeName")
            {
                result = ogreObject->getTypeName();
            }
            else if (field == "AmbientLight")
            {
                result = format::toString(ogreObject->getAmbientLight());
            }
            else if (field == "PlaneEnable")
            {
                result = format::toString(ogreObject->isSkyPlaneEnabled());
            }
            else if (field == "Plane")
            {
                Vector4 vp(plane->normal.x, plane->normal.y, plane->normal.z, plane->d);
                result = format::toString(vp);
            }
            else if (field == "BoxEnable")
            {
                result = format::toString(ogreObject->isSkyBoxEnabled());
            }
            else if (field == "DomeEnable")
            {
                result = format::toString(ogreObject->isSkyDomeEnabled());
            }
            else if (field == "FogMode")
            {
                result = format::toString(ogreObject->getFogMode());
            }
            else if (field == "FogColour")
            {
                result = format::toString(ogreObject->getFogColour());
            }
            else if (field == "FogStart")
            {
                result = format::toString(ogreObject->getFogStart());
            }
            else if (field == "FogEnd")
            {
                result = format::toString(ogreObject->getFogEnd());
            }
            else if (field == "FogDensity")
            {
                result = format::toString(ogreObject->getFogDensity());
            }
            else if (field == "ShadowTechnique")
            {
                int index = 0;
                auto st = ogreObject->getShadowTechnique();
                if (st == 0x12)
                {
                    index = 1;
                }
                else if (st == 0x11)
                {
                    index = 2;
                }
                else if (st == 0x22)
                {
                    index = 3;
                }
                else if (st == 0x21)
                {
                    index = 4;
                }
                else if (st == 0x25)
                {
                    index = 5;
                }
                else if (st == 0x26)
                {
                    index = 6;
                }
                result = format::toString(index);
            }
            else if (field == "ShadowColour")
            {
                result = format::toString(ogreObject->getShadowColour());
            }
            else if (field == "ShadowFarDistance")
            {
                result = format::toString(ogreObject->getShadowFarDistance());
            }
            return result;
        }

        virtual bool setValue(std::string field, std::string value)
        {
            if (field == "AmbientLight")
            {
                auto ambient = format::parseColourValue(value);
                ogreObject->setAmbientLight(ambient);
            }
            else if (field == "PlaneEnable")
            {
                auto check = format::parseBool(value);
                ogreObject->setSkyPlaneEnabled(check);
            }
            else if (field == "Plane" || field == "PlaneMaterial")
            {
                if (field == "Plane")
                {
                    auto v4 = format::parseVector4(value);
                    plane->normal.x = v4.x;
                    plane->normal.y = v4.y;
                    plane->normal.z = v4.z;
                    plane->d = v4.w;
                }
                else if (field == "PlaneMaterial")
                {
                    planeMaterial = value;
                }
                if (!planeMaterial.empty())
                {
                    ogreObject->setSkyPlane(true, *plane, planeMaterial);
                }
            }
            else if (field == "BoxEnable")
            {
                auto check = format::parseBool(value);
                ogreObject->setSkyBoxEnabled(check);
            }
            else if (field == "DomeEnable")
            {
                auto check = format::parseBool(value);
                ogreObject->setSkyDomeEnabled(check);
            }
            else if (field == "BoxMaterial")
            {
                if (!value.empty())
                {
                    ogreObject->setSkyBox(true, value);
                }
            }
            else if (field == "DomeMaterial")
            {
                if (!value.empty())
                {
                    ogreObject->setSkyDome(true, value);
                }
            }
            if (field == "FogMode" || field == "FogColour" || field == "FogStart" || field == "FogEnd" || field == "FogDensity")
            {
                if (field == "FogMode")
                {
                    fogMode = (FogMode)format::parseInt(value);
                }
                else if (field == "FogColour")
                {
                    fogColor = format::parseColourValue(value);
                }
                else if (field == "FogStart")
                {
                    fogStart = format::parseReal(value);
                }
                else if (field == "FogEnd")
                {
                    fogEnd = format::parseReal(value);
                }
                else if (field == "FogDensity")
                {
                    fogDensity = format::parseReal(value);
                }
                ogreObject->setFog(fogMode, fogColor, fogDensity, fogStart, fogEnd);
            }
            else if (field == "ShadowTechnique")
            {
                int index = format::parseInt(value);
                ShadowTechnique st = (ShadowTechnique)index;
                if (index == 1)
                {
                    st = ShadowTechnique::SHADOWTYPE_STENCIL_MODULATIVE;
                }
                else if (index == 2)
                {
                    st = ShadowTechnique::SHADOWTYPE_STENCIL_ADDITIVE;
                }
                else if (index == 3)
                {
                    st = ShadowTechnique::SHADOWTYPE_TEXTURE_MODULATIVE;
                }
                else if (index == 4)
                {
                    st = ShadowTechnique::SHADOWTYPE_TEXTURE_ADDITIVE;
                }
                else if (index == 5)
                {
                    st = ShadowTechnique::SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED;
                }
                else if (index == 6)
                {
                    st = ShadowTechnique::SHADOWTYPE_TEXTURE_MODULATIVE_INTEGRATED;
                }
                ogreObject->setShadowTechnique(st);
            }
            else if (field == "ShadowColour")
            {
                auto color = format::parseColourValue(value);
                ogreObject->setShadowColour(color);
            }
            else if (field == "ShadowFarDistance")
            {
                auto dist = format::parseReal(value);
                ogreObject->setShadowFarDistance(dist);
            }
            return true;
        }
    };
}
OgreSceneManager

  需要注意的是ShadowTechnique里我們KeyItem.xml文件里列出的項並沒與Ogre本身的對應,所以需要在這處理一下,如果是正常一一對應的,直接tostring與parseInt后就行. 然后別的Ogre對象分別實現的OgreTemplate泛型具體化類都差不多是這種處理.

  回頭我們再看PanelProperties中updateRelationFields與notifyAction的處理,前者是每楨根據當前NodeStyle的對應type找到對應的OgreTemplate泛型具體化子類.然后根據當前OgreStyle的OgreName找到對應Ogre對應,然后根據字段,或是更新,更新得到對應OgreTemplate泛型具體化子類中的值.

  至於為什么要每楨更新,而不是根據UI操作更新,簡單來說,是為了避免BUG產生,如上面我們點擊SceneNode的visible為flase后,對應節點下的MovableObject的visible也變成false,如果你不設置對應UI更新,對應的MovableObject上的visible還是顯示為true,如果你去更新UI,可以說這樣工作量又變大了,有一些你也想不到的關聯,這樣就等着更新大量的BUG去吧.其實這二種更新方式,對應我們官方的話來說:一個是事件驅動,一個是數據驅動.所以我們也不要以為WPF中的數據驅動是什么高大上的概念,就是因為如這里樣WPF關聯了控件與對應數據,數據更新后,每楨渲染時,根據數據顯示對應UI狀態.

  如上通過這三個部分,我們實現了控件根據XML自動生成,控件根據Ogre對象自動更新.

  當這個方案完成后,我只花了二個晚上的時間就完成了如上圖中的Ogre對象的各個UI界面更新,完成不需要更新任何有關UI控件的代碼,只要三個步驟就行,一是更新XML文件,二是具體化對應泛型OgreTemplate的實現,三是在OgreManager關聯Ogre對象與泛型OgreTemplate就行,在可見的將來,對應的粒子,地形,合成器的編輯都可以用這種方式.

  PS  2016.1.12 因為現在這個設計不滿足Ogre2.1,暫時沒有繼續完成下去的欲望,代碼放git上免的讓別人認為是搞笑,就直接放http://pan.baidu.com/s/1kTWdqnl 這里,vs2013,相應DLL已經組織好,給mygui與ogre新手參考.

 


免責聲明!

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



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