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新手参考.

 

posted @ 2015-08-31 00:30  天天不在  阅读(2704)  评论(1编辑  收藏  举报